虚拟机类加载机制之类的初始化时机

时间:2023-01-02 23:36:33

引言

  虚拟机把描述类的数据即Class文件加载到内存中,并对数据进行验证、转换解析和初始化,最终形成可以直接被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

类的生命周期

  虚拟机类加载机制之类的初始化时机

 

类初始化的时机

  第一个阶段加载是由虚拟机控制,对于初始化阶段,虚拟机严格要求有且只有5种情况触发初始化。

  1)遇到new、putstatic、getstatic、invokestatic指令时触发,也就是使用new关键字实例化对象时、读取和设置类的静态字段(被final static修饰存入常量池的除外)、调用类的静态方法。

  2)使用java.lang.reflect包的方法对类进行反射调用时触发。

  3)初始化一个类时发现其父类未初始化时会初始化其父类。

  4)虚拟机启动时,用户需指定一个主类(含有main方法的类),首先初始化该主类。

  5)使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果是REF_getstatic、REF_petstatic、REF_invokestatic的方法句柄,并且该方法句柄对应的类没有初始化时触发。

  上述五种情况属于主动引用,还有被动引用不会触发初始化。

  被动引用案例1

package com.wjz.demo;
public class SuperClass {
public static int i = 1;
static {
System.out.println(
"SuperClass init");
}
}
package com.wjz.demo;
public class SubClass extends SuperClass {
static {
System.out.println(
"SubClass init");
}
}
package com.wjz.demo;
public class NotInitialization {
/**
* 非主动使用类字段
*/
public static void main(String[] args) {
System.out.println(SubClass.i);
}
}

输出结果

SuperClass init
1

  对于静态字段只有直接定义该字段的类会被初始化,通过子类引用父类的静态字段只会初始化父类。

  被动引用案例2

package com.wjz.demo;
public class NotInitialization {
/**
* 通过数组定义来引用类,不会初始化该类
*/
public static void main(String[] args) {
SuperClass[] scs
= new SuperClass[10];
}
}

  运行结果是什么都没有,说明没有初始化SuperClass类,但是虚拟机触发了另外一个名为“[Lcom.wjz.demo.SuperClass”的类的初始化阶段,这是由虚拟机自动生成的,创建动作由newarray指令触发。

  被动引用案例3

package com.wjz.demo;
public class ConstantClass {
public final static String val = "Hello";
static {
System.out.println(
"ConstantClass init");
}
}
package com.wjz.demo;
public class NotInitialization {
/**
* 常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,不会初始化定义常量的类
*/
public static void main(String[] args) {
System.out.println(ConstantClass.val);
}
}

  运行结果只输出了“Hello”,这是因为在编译阶段通过常量传播优化,“Hello”存储到了NotInitialization类的常量池中,之后对常量ConstantClass.val的引用实际都转化为了NotInitialization对自身常量池的引用,NotInitialization的Class文件中没有ConstantClass类的符号引用入口。