JVM加载class字节码的过程可以分为加载、连接(验证、准备,解析)、初始化这三(五)个过程,本文研究的是初始化过程。
- 对于静态字段,只有直接定义该字段的类才会被初始化(虽然是通过子类的类名来调用)
image.png
- 在编译输出目录下,通过命令行输入
java -verbose com.aksr.MyTest1查看类的加载情况,运行结果如下
com.aksr.MyParent1 加载、初始化
com.aksr.MyChild1 加载
image.png
-
调用子类静态变量时,会导致父类先初始化,然后子类初始化
image.png
- 同上,通过
java -verbose com.aksr.MyTest1命令得运行知结果如下
com.aksr.MyParent1 加载、初始化
com.aksr.MyChild1 加载、初始化
- 对于final修饰的静态常量,如果编译期其值可以确定,则该常量会被放入调用它的方法所在的类的常量池中,此时调用不会引起定义该常量的类的初始化
image.png
-
通过
javap -c com.aksr.MyTest2反编译字节码文件,如图所示
ldc助记码表示将int, float或String型常量值从常量池中推送至栈顶,其中"hello world"字符串在MyTest2.class常量池中的符号引用为#4
image.png -
查看类加载的情况,可以发现,MyParent2类并没有被JVM加载(和初始化),即使删除MyParent2.class文件,也可以正常运行MyTest2.class
image.png
-
同理,如果final修饰的常量在编译期不能够确定(如需要进行函数的调用),则会引起类的加载和初始化
image.png -
对于接口来说,接口内的字段都是public static final修饰的,所以和上面的结论相同,常量a的值在编译期可以确定,所以会被放到MyTest3.class的常量池中,不会引起MyParent3的加载和初始化
image.png
-
如果接口内的常量值,编译期不能确定,会引起接口的加载与初始化
image.png
-
子类接口的的初始化不会导致父类接口初始化
image.png
-
但是会导致父类的加载
image.png
总结
使用静态字段不会导致类被加载的情况:该静态字段有final修饰(常量),并且编译期其值可确定
使用静态字段不会导致类被初始化的情况:该静态字段只在父类中定义
子类被初始化前,父类一定会被初始化;子接口初始化,不会导致父接口初始化,但会导致其加载










