虚拟机把Class文件加载到内存,经过校验、解析和初始化,最终转换成虚拟机可以使用的Java类型,这就是Java的虚拟机类加载机制。
总体流程如图
如果类没有初始化,那么以下几种方式会触发类的加载
这里有两个注意的点
SubClass[] classArray = new SubClass[10];
public static final String value = "SubClass value";
还有一点,子接口的初始化不会影响父接口,只有用到父接口,比如父接口中定义的常量,才会触发父接口的初始化
通过类的全限定名获取这个类的二进制字节流,在内存中生成代表这个类的class对象
验证class字节流是否合法。
因为class字节流不一定是java源码编译而来,所以虚拟机需要保证class合法的,不会对载入了错误的字节流导致崩溃
为类变量准备内存。
设置static变量的初始值,但跟真正的初始化有区别,此处只是赋一个默认值
public static int value = 123;
比如这种,会赋0。但是如果是这种,就会直接赋值
public static final int value = 123;
将符号引用转换为直接引用。
符号引用:一组用来描述目标的符号,只要使用时能定位到目标即可
直接引用:直接指向目标的指针或句柄
解析内容
调用cinit方法,进行变量的初始化。
cinit方法是编译器收集的所有变量赋值操作和static代码块合并而成,顺序是按源文件中的顺序决定的
虚拟机会保证多线程环境下的cinit方法的执行,多个线程访问,只有一个线程可以调用cinit方法,这就是内部类实现单例的原理
使用与卸载就不多说了,当虚拟机发现没有对该类型的引用的时候,会触发类的卸载
什么是类加载器?通过一个类的全限定名来获取这个类的二进制流,放到java虚拟机外部去实现,以便让应用程序自己决定如何去加载这个类
比较两个类是否相等,只有判断两个类,只有判断在同一个类加载器下才有意义,即使是同一个class对象,用不同的类加载器加载,那么这个类 必不相等
启动类加载器:JAVA_HOME\lib类库加载到虚拟机内存中
扩展类加载器:JAVA_HOME\lib\ext 目录中
应用程序类加载器:系统类加载器,加载用户类路径上所制定的类库。默认类加载器
重写loadclass方法,特殊的类自己处理,其他的调用super方法
《深入理解JAVA虚拟机》