类加载机制:指的是虚拟机将 描述类的数据 从 .class 文件中加载到内存,并进行校验,解析和初始化,形成 Java 虚拟机能够直接使用的数据类型的机制
在这个步骤中,类加载器会寻找 .class 文件,打开并读取文件数据到内存中,并转换成方法区运行时数据,并在堆中创建一个 Class 对象 作为方法区数据访问的入口
链接也包含三个步骤: 验证, 准备,解析
这个过程会完成静态变量 (类对象) 的真正初始化(代码中赋为何值, 此时就赋为何值), 执行静态代码块。在初始化一个类的时候,如果发现该类的父类还没初始化,会优先初始化父类。
在类加载中的 加载阶段中, 类加载器是怎样寻找 .class 文件的?在 JVM 中是通过双亲委派模型来完成该任务。
在 JVM中,会由类加载器来完成这个任务,包括三个类加载器:BootStrapClassLoader,ExtensionClassLoader 和 ApplicationClassLoader
JVM 在这三个类中定义了父子关系,从上到下,上面是下面的父亲,例如,BootStrapClassLoader 是ExtensionClassLoader 的父亲,ExtensionClassLoader 是 ApplicationClassLoader 的父亲。
定义完这个关系之后,遵循一个规则:当子类加载类的时候,会先交给自己的父亲,父亲再交给它的父亲,如果没有父亲,那么就由该类先扫描自己的目录,如果能完成加载,那就完成任务,否则再交给自己的子类扫描,是一个从下到上,再从上往下扫描的过程
例如:对于拓展库(在ExtensionClassLoader 目录中)中的类, JVM 会先调用 ApplicationClassLoader 这个加载器,这个加载器并不会立刻扫描自己的目录,而是交给自己的父亲 ExtensionClassLoader,它也不会立刻扫描自己,而是再交给自己的父类 BootStrapClassLoader,这个类加载器没有父亲了,所以开始扫描自己,但是由于在拓展库中,所以自然扫描不到,再交返给自己的孩子 ExtensionClassLoader 加载,显然至此任务完成。如果所有类加载器都无法加载某个类,就会抛出 ClassNotFount 异常
看到这,我们可以假设一下,如果刚开始 ApplicationClassLoader 直接扫描自己目录中的类,会出现啥情况?
假设我们自己定义了一个和标准库同名的类:String,这时如果 ApplicationClassLoader 立刻加载这个类,就会导致 BootStrapClassLoader 中要加载的 String 类被覆盖。因此 JVM 要优先保证高优先度的类能够被正确加载
除此之外,双亲委派模型还能防止同个类被多次加载。