一道有趣的類加載面試題

題目

運行以下代碼的Test1與Test2分別輸出什麼結果java

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結果解析:

  1. 因爲在mian方法中打印語句調用的是Child.FINAL_STR變量。
  2. 從Child的類中能夠得知,FINAL_STR爲final而且爲static變量,其在調用static final變量的時候不會觸發類的初始化操做。因此結果如上

Test2結果解析:

  1. 因爲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
}
  1. 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加載狀況

經過添加JVM參數-XX:+TraceClassLoading能夠查看類加載狀況優化

可見,會對Child,Parent類進行類加載操做,可是調用static方法,只有Parent類會調用static進行初始化操做。code

總結

  1. 若是引用了常量池變量(String,以及基本類型相關變量),若是該變量爲final static進行修飾的時候,則不會對類進行初始化操做
  2. 若是爲很是量池變量,若是調用方存在父子類關係,則實際JVM會加載子類與父類,可是若是使用的爲父類的final變量,並不會觸發類的初始化操做。
相關文章
相關標籤/搜索