題目
運行如下代碼的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變量,並不會觸發類的初始化操作。