题目
运行如下代码的 Test1 与 Test2 分别输出什么结果
- public class Parent {
- static {
- System.out.println("Parent static invoke");
- }
- public static final String FINAL_STR="FINAL_STR";
- public static final Test FINAL_OBJECT=new Test();
- public Parent(){
- System.out.println("Parent init");
- }
- }
- public class Child extends Parent {
- static {
- System.out.println("Child static invoke");
- }
- public Child(){
- System.out.println("child init");
- }
- }
- public class Test {
- public static void main(String[] args) {
- System.out.println(Child.FINAL_STR);
- }
- }
- public class Test2 {
- public static void main(String[] args) {
- System.out.println(Child.FINAL_OBJECT);
- }
- }
结果:
运行 Test1 结果
FINAL_STR
运行 Test2 结果
- Parent static invoke
- cn.lonecloud.Test@5e2de80c
解析:
Test1 结果解析:
由于在 mian 方法中打印语句调用的是 Child.FINAL_STR 变量.
从 Child 的类中可以得知, FINAL_STR 为 final 并且为 static 变量, 其在调用 static final 变量的时候不会触发类的初始化操作. 所以结果如上
Test2 结果解析:
由于 Test2 中引用的对象为父类 Parent 的静态变量, 由于并不是常量池中的对象, 所以, 会触发 Parent 的初始化操作.
深究:
Test1 字节码:
- Compiled from "Test.java"
- public class cn.lonecloud.Test {
- public cn.lonecloud.Test();
- Code:
- 0: aload_0
- 1: invokespecial #1 // Method java/lang/Object."<init>":()V
- 4: return
- public static void main(java.lang.String[]);
- Code:
- 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
- 3: ldc #4 // String FINAL_STR
- 5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
- 8: return
- }
- ldc #4 // String FINAL_STR
其为获取静态化变量方法, 其为将常量压入栈中, 由于静态变量在 JVM 中存在常量池的概念, 会将字符串进行优化, 所以并不会触发类初始化
Test2 字节码:
- Compiled from "Test.java"
- public class cn.lonecloud.Test {
- public cn.lonecloud.Test();
- Code:
- 0: aload_0
- 1: invokespecial #1 // Method java/lang/Object."<init>":()V
- 4: return
- public static void main(java.lang.String[]);
- Code:
- 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
- 3: getstatic #3 // Field cn/lonecloud/Child.FINAL_OBJECT:Lcn/lonecloud/Test;
- 6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
- 9: return
- }
由于需要使用到 Child 类的父类中的 FINAL_OBJECT 变量, 未使用到 Child 类中的变量, 所以不会对 Child 类进行初始化, 初始化的为其父类.
查看 JVM 加载情况
通过添加 JVM 参数 - XX:+TraceClassLoading 可以查看类加载情况
可见, 会对 Child,Parent 类进行类加载操作, 但是调用 static 方法, 只有 Parent 类会调用 static 进行初始化操作.
总结
如果引用了常量池变量 (String, 以及基本类型相关变量), 如果该变量为 final static 进行修饰的时候, 则不会对类进行初始化操作
如果为非常量池变量, 如果调用方存在父子类关系, 则实际 JVM 会加载子类与父类, 但是如果使用的为父类的 final 变量, 并不会触发类的初始化操作.
来源: https://www.cnblogs.com/lonecloud/p/11757316.html