Java的类加载器ClassLoader

Java类加载方式采取树形结构的双亲委托机制。主要分为4种类加载器:Bootstrap、ExtClassLoader、AppClassLoader、自定义类加载器,如下图:

树形结构.png

Bootstrap:加载rt.jar中所有的类,C/C++编写,Java中不存在该类
ExtClassLoader:加载ext目录下所有的扩展类
AppClassLoader:加载应用类,一般为我们编写的字节码
MyClassLoader:自定义类加载器

类加载器的具体规则,看下图:


image.png

如上图,加载类的时候,先看自身的ClassLoader是否已经加载过该类,加载过就直接获取;如果未加载过,则看父加载器是否已经加载过该类......以此类推。
另外需要注意,被当前类引用的类的加载应该由加载当前类的ClassLoader或者父加载器加载,否则会抛异常。

比如:如果一个类是用AppClassLoader加载的,我们把该类转移到ext目录下,该类继承的类本来也是AppClassLoader加载,由于我们把该类交由给ExtClassLoader加载了,按照加载规则,ExtClassLoader未加载到继承的类,抛异常ClassNotFoundException。

看代码示例:

public class ClassLoaderTest {

    public static void main(String[] args) {
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader.getClass().getName());
        System.out.println(int.class.getClassLoader());
        System.out.println(String.class.getClassLoader());
        System.out.println(System.class.getClassLoader());

        while (classLoader != null){
            System.out.println(classLoader.getClass().getName());
            classLoader = classLoader.getParent();
        }
    }
}

打印结果:

sun.misc.Launcher$AppClassLoader
null
null
null
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader

需要注意的是,类的加载器的父亲并不是其真正的父类,看JDK源码得知,AppClassLoader和ExtClassLoader都属于Launcher类的静态内部类,都继承于URLClassLoader,继承关系如下图:

image.png

image.png

源代码简略如下:

public class Launcher {

    static class AppClassLoader extends URLClassLoader {
        ...
    }

    static class ExtClassLoader extends URLClassLoader {
        ...
    }

}

那么如何自定义一个类加载器呢?
很简单,只需要继承ClassLoader类,并复写findClass()方法即可。
注意:自定义类加载器的默认父加载器是AppClassLoader。
代码如下:

public class MyClassLoader extends ClassLoader {

    private String path;

    public MyClassLoader(String path) {
        this.path = path;
    }

    /**
     * @param name 类的全路径(包名+类名),比如cn.tl.domain.Employee
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("MyClassLoader findClass execute...");

        String filePath = path + name.replace('.', File.separatorChar) + ".class";
        try (
                FileInputStream fis = new FileInputStream(filePath);
                ByteArrayOutputStream bos = new ByteArrayOutputStream()
        ) {
            int len;
            while ((len = fis.read()) != -1) {
                bos.write(len);
            }
            byte[] bytes = bos.toByteArray();
            return defineClass(name, bytes, 0, bytes.length);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return super.findClass(name);
    }
}

测试类:

public class MyClassLoaderTest {

    public static void main(String[] args) throws Exception{
        Class<?> aClass = new MyClassLoader("F:\\").loadClass("cn.tl.domain.Employee");
        System.out.println(aClass.getClassLoader().getClass().getName());
        Object o = aClass.newInstance();
        System.out.println(o);
    }
}

打印结果:

sun.misc.Launcher$AppClassLoader
Employee [name=null, age=0]

有人可能会发现控制台并没有打印出MyClassLoader findClass execute...,类也是AppClassLoader加载的,我们自定义的加载器根本没有起作用。请删除工作空间编译后的class文件!!!,否则AppClassLoader加载到该类,就终止加载流程了,根本就没MyClassLoader的事了。
删除工作空间的class文件后的打印结果:

MyClassLoader findClass execute...
cn.tl.classloader.MyClassLoader
Employee [name=null, age=0]

到这里,再思考一个问题:为什么类加载是双亲委托机制呢?
因为如果不委托,我们自定义跟JDK一样的类,再自定义一个类加载器加载,内存中就会存在两份同样的字节码了,可以防止JDK核心类被篡改。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 1 基本信息 每个开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,...
    java小菜鸟阅读 2,580评论 0 15
  • 首先声明,我是因为看了ImportNew网站上的几篇关于类加载器的文章之后,才萌生了写这篇文章的想法。所以在写这篇...
    陈安妮annie1阅读 253评论 0 1
  • 首先声明,我是因为看了ImportNew网站上的几篇关于类加载器的文章之后,才萌生了写这篇文章的想法。所以在写这篇...
    java大哥阅读 657评论 0 2
  • ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于某些框架开发者来说却非常常见...
    时待吾阅读 1,028评论 0 1
  • 都是时光飞逝啊,一转眼第五期冥想族也要结营了上期结营还历历在目,我们就是如此老去的吧! 时间关系,简单说说几点收获...
    宇文东林阅读 749评论 5 30