类加载机制
什么是类加载机制?
Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。
每个Class文件代表着一个类或者接口。
类加载的时机
一个类在虚拟中的经历为七个阶段:加载,验证,准备,解析,初始化,使用,卸载。 其中验证,准备,解析统称为连接。
加载,验证,准备,初始化,卸载这五个过程的执行的先后是确定的,注意不一定会先后结束,可能在一个阶段的过程中开始另一个阶段。
第一个过程也就是加载,什么时间开始有虚拟机自行决定。对于初始化则严格规定了有且只有六种情况必须对类立即进行初始化:
- 遇到new,getstatic,putstatic,invokestatic这四条字节码指令时,如果类没有进行初始化,则需要先出发其初始化阶段。生成这四条指令的Java场景:
①使用new关键字实例化对象
②读取或设置一个类型的静态字段的时候,被final修饰,已在编译器把结果放入常量池的静态字段除外。
③调用一个类的静态方法的时候 - 使用 java.lang.reflect 包的方法对类型进行反射调用的时候,如果类型没有进行过初始化,则先初始化。
- 初始化类的时候若是父类没有初始化,则先触发父类的初始化。
- 虚拟机启动时,对用户指定的 主类(包换main()方法的那个类)进行初始化。
- JDK7中动态语言支持,一个 java.lang.invoke.MethodHandle 实例最后解析结果为 REF_getStatic,REF_putStatic,
REF_invokeStatic,REF_newInvokeSpecial四种类型的方法句柄,并且对应的类没有初始化,则先触发其初始化。 - 当一个接口定义了JDK8 中的默认方法时(default 修饰的方法),如果这个接口的实现类发生了初始化,则接口要在其之前初始化。
这六种场景的行为成为对一个类型的主动引用,除此之外的引用方式都不会触发初始化,称为被动引用。
例:
- 通过子类引用父类的静态字段不会对子类进行初始化,只会初始化父类。
- 对象数组不会触发对象的初始化。People[] people = new People[10];
- 对类中常量池中的变量进行引用,不会调用类的初始化。因为常量在编译阶段就会存入常量池中(final static)。
接口与类初始化唯一的不同是第三个场景,接口无需对父接口初始化,只有用到时才会。