什么是类加载,又有哪些类加载器你真的了解吗?

Posted 你这家伙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是类加载,又有哪些类加载器你真的了解吗?相关的知识,希望对你有一定的参考价值。

类加载

1.1.什么是类加载?

就是.java代码通过javac编译生成.class文件(文件是保存在磁盘上的),如果想要JVM使用这个类,那就需要将这个类加载到内存中,这个过程成为“类加载”。

三个步骤:

  • **1. 加载(loading):**读取.class文件,按照格式来进行解析(.class文件是有固定的格式,javac生成的时候就会按照这个格式来进行组织,JVM加载的时候也会按照这个格式来进行解析)
  • **2. 连接(linking):**一个类可能会依赖其他类,如果想要使用这个类,需要将这个类依赖的类也需要加载
  • **3. 初始化:**创建类对象,初始化该类的静态成员,并且执行静态代码块中的内容

注意:
类型的加载,连接,初始化都是在程序运行期间完成的,(类型:指的是定义的类,接口或者枚举成为类型而不涉及对象,在类加载的过程中,就是一个创建对象之前的一些信息)

示例:

class A{
    public A(){
        System.out.println("A的构造");
    }
    {
        System.out.println("A的构造块");
    }
    static {
        System.out.println("A的static块");
    }
}
class B extends  A{
    public B(){
        System.out.println("B的构造");
    }
    {
        System.out.println("B的构造块");
    }
    static {
        System.out.println("B的static块");
    }
}
public class Test extends B{
    public static void main(String[] args) {
        System.out.println("开始");
        new B();
        new B();
        System.out.println("结束");
    }
}

此时你先可以不用看下面,看看你自己得到的答案是什么,看你是否对这个已经掌握?
输出结果为

对此知道的人就是知道了,不知道的你可能还在疑惑当中,别急,听我细细道来

示例修改:

如果此时Test不继承B,那么输出结果又是如何呢?上面回答错误的老铁给个机会,这次在思考看看有没有进步嘞
输出结果:

结果分析:

1.2.Java中有哪些类加载器?

就是JVM里已经实现好了的类,通过这些特殊的类来具体执行类加载的过程

默认主要有三个类加载器(用户也可以自定新的类加载器):

  • BootstrapClassLoader:专门加载java标准库中的类
  • ExternsionClassLoader:加载一些特殊的类
  • ApplicationClassLoader:加载用户自己写的类
  1. BootstrapClassLoader加载器
    那么标准库的类都在哪里呢?
    如果类在jdk1.8 / jre / lib / rt.jar(可以用压缩包打开),这个文件下,那么就可以使用BootstrapClassLoader加载器来进行加载

  2. ExternsionClassLoader加载器

因为他是加载特殊的类,所以这里就找一个特殊的类来示例
如果类在jdk1.8 / jre / lib / ext目录下,那么就需要通过ExternsionClassLoader加载器来加载

  1. ApplicationClassLoader加载器
    如果类在CLASS_PATH环境变量,或者java -cp指定的目录,或者在当前的目录中,就使用ApplicationClassLoader加载器进行加载

1.3.什么是“双亲委派模型”/“双亲委派原则”?

上面所提到的类加载器之间,其实具有父子关系(但是不是通过类的继承)

  • BootstrapClassLoader 是 ExternsionClassLoader 的父亲
  • ExternsionClassLoader 是 ApplicationClassLoader 的父亲

其实在进行类加载的时候,有一个约定好的顺序

以加载String为例

  1. JVM需要加载类的时候,无论是这个类是什么样的,统一都从Application开始执行
  2. 把类名(java.lang.String)告诉给Application ,Application自身不会真的取进行加载,而是先去找他的父亲Externsion
  3. 当Externsion 拿到这个类名之后同样也不会真的取执行,而是在把任务交给他的父亲Bootstrap
  4. Bootstrap 拿到这个类名的时候,此时它上面没有父亲了,那就只能亲自己加载
  5. 由于String类就在rt.jar 里,此时Bootstrap就加载成功
    6.此时的Externsion 和Application就不需要在进行其他工作了
    总结:遇到类名,先找爸爸,爸爸搞不定,再自己进行加载,如果到最后都找不到就会抛出一个ClassNotFoundExceptin的异常

1.4.自己创建的类加载器是否也要遵循双亲委派模型?是否可以违背?

可以违背,不一定非要遵守,只是标准库中的三个类加载器遵循双亲委派模型
(比如tomcat中的类加载器就没有遵守,webapps 目录中要放很多的.class文件,tomato在执行的时候,实现了自己的类加载器,专门负责从指定的webapps目录中加载对应的类,那么此处就不存在“传递给父类加载器”)

常见面试题

1.什么时候会触发某个类的加载?
答:当使用到一个类的时候,并且该类不在内存中,这个时候就需要加载。那么使用到一个类的时候具体是什么时候呢?

  1. 构造类的实例(new实例的时候)
  2. 使用该类的静态属性/方法
  3. 使用子类的时候,也会触发父类的加载。
    可以综合上面的例题进行分析

看到了写的不错的文章,可供参考

以上是关于什么是类加载,又有哪些类加载器你真的了解吗?的主要内容,如果未能解决你的问题,请参考以下文章

面试必问的 JVM 类加载机制,你真的了解吗?

面试必问的 JVM 类加载机制,你真的了解吗?

面试必问的 JVM 类加载机制,你真的了解吗?

面试必问的 JVM 类加载机制,你真的了解吗?

类加载机制你真的了解吗?

jvm入门