什么是类加载机制?类加载和类型的加载不同。
类加载机制的5个阶段?这些阶段通常都是互相交叉地混合进行的。解析阶段的顺序可能在初始化之后。
类的初始化阶段有且只有六种主动引用情况
1、遇到new、 getstatic、 putstatic或invokestatic这四条字节码指令时
使用new关键字实例化对象的时候
读取或设置一个类型的静态字段
调用一个类型的静态方法的时候
2、进行反射调用的时候
3、当初始化类的时候, 如果发现其父类还没有进行过初始化
4、指定一个要执行的主类( 包含main()方法的那个类)
5、四种方法句柄
6、当一个接口中定义了JDK 8新加入的默认方法( 被default关键字修饰的接口方法) 时, 如果有其实现类发生了初始化, 那该接口要在其之前被初始化
还有三种被动情况不会初始化
1、通过子类引用父类的静态字段和静态方法, 不会导致子类初始化
2、通过类的数组定义来引用类, 不会触发此类的初始化
3、通过类名+常量来调用常量时,不会触发此类的初始化
接口的初始化阶段
接口与类真正有所区别的是 当一个类在初始化时, 要求其父类全部都已经初始化过了, 但是一个接口在初始化时, 并不要求其父接口全部都完成了初始化, 只有在真正使用到父接口的时候(如引用接口中定义的常量) 才会初始化。
类加载机制的具体过程
1、加载,使用类加载器对类进行加载,分为三步。
非数组类型的加载阶段是开发人员可控性最强的阶段
数组类型是由虚拟机动态运行生成的,但是数组类的元素类型最终还是要靠类加载器来完成加载
数组类型的创建过程:
如果组件类型是引用类型
如果组件类型不是引用类型
2、验证的目的是,分为四个动作,其中符号引用验证发生在解析阶段
3、准备的目的是为静态变量分配内存并设置类变量初始值 ,初始值通常是零值,但有ConstantValue属性的例外
4、解析的作用是;除invokedynamic指令以外, 虚拟机实现可以对第一次解析的结果进行缓存;
对类或接口的解析:
1、如果C不是一个数组类型, 那虚拟机将会把代表N的全限定名传递给D的类加载器去加载这个类C ,
2、如果C是一个数组类型, 并且数组的元素类型为对象,那将会按照第一点的规则加载数组元素类型。 如果元素类型不是对象,由虚拟机生成一个代表该数组维度和元素的数组对象。
3、符号引用验证
对字段的解析:
1、首先对字段所属类或接口进行解析,
2、如果类或接口本身有相匹配符号引用的字段,直接返回直接引用
3、如果类或接口实现了接口, 将会按照继承关系从下往上递归搜索各个接口和它的父接口,有匹配返回直接引用
4、如果类或接口不是Object类,将会按照继承关系从下往上递归搜索其父类 ,有匹配返回直接引用
5、符号引用验证
对方法的解析:
1、首先对方法所属类进行解析
2、检查方法的符号引用的class_info项是不是接口,是的话异常
3、在类中查找匹配的符号引用的方法,有则返回其直接引用
4、在父类中查找匹配的符号引用的方法,有则返回其直接引用
5、在类的接口中查找匹配的符号引用的方法,有则返回其直接引用
6、符号引用验证
对接口方法的解析:
1、首先对方法所属接口进行解析
2、检查方法的符号引用的class_info项是不是类,是的话异常
3、在接口中查找匹配的符号引用的方法,有则返回其直接引用
4、在父接口中查找匹配的符号引用的方法,有则返回其直接引用
5、符号引用验证
5、初始化阶段就是执行类构造器<clinit>()方法的过程,<clinit>()并不是程序员在Java代码中直接编写的方法, 它是Javac编译器的自动生成物;
<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的;
<clinit>()方法与类的构造函数(即在虚拟机视角中的实例构造器<init>()方法) 不同,它不需要显式地调用父类构造器 ;
执行接口的<clinit>()方法不需要先执行父接口的<clinit>()方法,因为只有当父接口中定义的变量被使用时, 父接口才会被初始化。 此外, 接口的实现类在初始化时也一样不会执行接口的<clinit>()方法。
同一个类加载器下, 一个类型只会被初始化一次。
类加载器
有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到Java虚拟机外部去实现, 以便让应用程序自己决定如何去获取所需的类。 实现这个动作的代码被称为“类加载器”;
比较两个类是否“相等”, 只有在这两个类是由同一个类加载器加载的前提下才有意义
三层类加载器是启动/引导类加载器,扩展类加载器,系统类加载器;
启动类加载器负责加载存放在<JAVA_HOME>\lib目录下的类库,负责java平台核心类库,无法被Java程序直接引用,可以用null值代替。
扩展类加载器负责加载<JAVA_HOME>\lib\ext目录中, 或者被java.ext.dirs系统变量所指定的路径中所有的类库,可以直接在程序中使用扩展类加载器
系统类加载器负责加载用户类路径上所有的类库, 开发者同样可以直接在代码中使用这个类加载器
双亲委派模型
是什么?工作流程是?好处是Java中的类随着它的类加载器一起具备了一种带有优先级的层次关系