类装载器详解

在讲解类装载器之前 我想先从一个比较实际的问题入手 我相信这也是所有Java新手想要问的一个问题:
一个Java程序到底是怎么个执行过程?

你会怎么回答呢?我相信大部分人是吞吞吐吐支支吾吾的说:先javac成字节码.class文件然后java执行啊! 至于接下来干什么 你问JVM去吧…
如果是在面试 恭喜你成功的逗笑了你的听众..
还是废话少说先来看什么是类装载器吧:

类装载器顾名思义 就是将类按需地装载 它由三部分组成:

  1. BootStrapClassLoader(根类加载器)
    根类加载器是一个由C++编写的JVM内核的一部分 它本身不需要再有其他的类装载(深入理解这部分需要翻阅JVM的源码)它负责加载%JAVA_HOME%/jre/lib下的jar包

  2. ExtClassLoader(扩展类加载器)
    扩展类加载器的工作是加载%JAVA_HOME%/jre/lib/ext下的jar包 如果想在命令窗口让javac XXX命令直接能运行而不用关心XXX所在的位置 那么就需要将XXX.java文件压缩成.zip格式的压缩文件 然后拷贝到扩展类加载器的家在目录下(包名要去掉)

  3. AppClassLoader(系统类加载器)
    系统类加载器是这三种类加载器中最”接地气”的一个 它的工作是加载classpath中的jar包 当有新类需要被初始化时 也是先交给AppClassLoader来加载

    这里为什么要用一个”先”字呢?这是因为类加载器之间存在一个”延迟加载”的加载策略 什么是”延迟加载” 延迟加载类似于设计模式中的懒汉式 只有当类需要被初始化的时候类加载器才会开始工作 三种类加载器之间还存在一个叫做”委托机制”的工作模式 即当类初始化的时候 系统类加载器会首先被要求进行类加载 而系统类加载器会首先询问一下扩展类加载器 扩展类加载器也同样会先询问一下根类加载器 就好像三个孩子–哥哥ExtClassLoader扩展类加载器(E) 弟弟AppClassLoader系统类加载器(A)和表哥BootStrapClassLoader根类加载器(B)三个人一块上街买东西 老板拿了东西问弟弟(A):一共两块五 弟弟抖了个激灵问哥哥(E):你有钱吗? 意思很明显 哥哥觉得很烦但又不能打人 用手捂住口袋看向表哥(B) 老哥你来掏 表哥(B)是个老实人不会说谎 如果他有钱(在自己的目录下有对应jar包存在) 它就只能掏钱付账三个人离开 反之如果真没带钱 它就会告诉哥哥(E):我没钱 你哥俩看着办 这时候哥哥(E)也只能摸摸口袋找钱 哥仨虽然喜欢互相推托但都是诚实的好孩子 如果哥哥也真没钱 弟弟(A)也只能自认倒霉了 弟弟的口袋里一定会有钱(类初始化总得有人管) 也就是这个账一定要有人付的
    到这里细心的人会发现一个现象:为什么三者的关系是表哥 哥哥和弟弟而不是父子孙?我相信很多人会有这种误解 请先看以下代码:

ClassLoader sys = ClassLoader.getSystemClassLoader();
System.out.println("系统类加载器:" + sys);

ClassLoader ext = sys.getParent();
System.out.println("扩展类加载器:" + ext);

ClassLoader root = ext.getParent();
System.out.println("根类加载器:" + root);

这里容易让人误解的地方就是getParent()方法的使用 其实三者之间并没有直接的继承与被继承的关系 一段代码来验证:

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        System.out.println("AppClassLoader的类加载器是: " + classLoader.getClass().getClassLoader());
        System.out.println("ExtClassLoader的类加载器是: " + classLoader.getParent().getClass().getClassLoader());
        Hello hello = new Hello();
        ClassLoader clal = Hello.class.getClassLoader();
        System.out.println("Hello类的类加载器是 " + clal.getClass());
        System.out.println("----------------------------------------------");
        Class clazz = clal.getClass();
        while(null != clazz.getSuperclass()){
            System.out.println(clazz + "的父类是" + clazz.getSuperclass());
            clazz = clazz.getSuperclass();
        }
        System.out.println("----------------------------------------------");
        ClassLoader claz = ClassLoader.getSystemClassLoader().getParent();
        Class clazzz = claz.getClass();
        while(null != clazzz.getSuperclass()){
            System.out.println(clazzz + "的父类是" + clazzz.getSuperclass());
            clazzz = clazzz.getSuperclass();
        }

这里的Hello类因为不在根和扩展类的加载目录下 所以它的类加载器是系统类加载器 是不是很多人不理解扩展类加载器和系统类加载器的类加载器为什么为null 或者是不是很多人认为这两个类加载器是不需要被其他类加载器加载的? 其实并不是 扩展类加载器和系统类加载器也是两个java类 当然也需要被加载器加载 他们的类加载器其实就是根类加载器 只不过根类加载器是用C++编写并且存在于JVM内核的 所以返回null就不足为奇了 唯一不需要加载器加载的只有根类加载器

这里需要我们看清类加载器和父类之间的区别 从上面的代码我们能够看出来 ExtClassLoader和AppClassLoader在继承关系上就是兄弟的关系 可以用下图来表示:
继承关系:
Java.lang.Object
— java.lang.ClassLoader
— java.security.SecureClassLoader
— java.NET.URLClassLoader
— sun.misc.Launcher$ExtClassLoader

java.lang.Object
— java.lang.ClassLoader
— java.security.SecureClassLoader
— java.net.URLClassLoader
— sun.misc.Launcher$AppClassLoader

    现在应该理解他们的关系了吧

最后附上各个类加载器的加载路径:

ClassLoader sys = ClassLoader.getSystemClassLoader();
        System.out.println("系统类加载器:" + sys);

        ClassLoader ext = sys.getParent();
        System.out.println("扩展类加载器:" + ext);

        ClassLoader root = ext.getParent();
        System.out.println("根类加载器:" + root);
        System.out.println("----------------------------------------------");
        //Java 根类加载器加载路径
        System.out.println("根类加载器加载路径: "+System.getProperty("sun.boot.class.path"));  
        //扩展类加载器的加载路径
        System.out.println("扩展类加载器的加载路径: "+System.getProperty("java.ext.dirs"));  
        //Java 系统类加载器加载路径
        System.out.println("系统类加载器加载路径: "+System.getProperty("java.class.path"));  
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值