1. 程式人生 > 其它 >第四篇 JVM之類載入時機

第四篇 JVM之類載入時機

Java程式對類的使用分為主動使用和被動使用,主動使用時,會觸發類的初始化(在JVM虛擬機器規範中,對於類在什麼時候載入並沒有做限定,但是對類的初始化時機有規定),而被動使用則不會。

Java程式對類的使用分為主動使用和被動使用,主動使用時,會觸發類的初始化(在JVM虛擬機器規範中,對於類在什麼時候載入並沒有做限定,但是對類的初始化時機有規定),而被動使用則不會。


主動引用

主動引用有七種情況:

  • 1、建立類的例項。
  • 2、訪問某個類或者介面的靜態變數,或者對該靜態變數進行賦值(被final修飾的靜態變數、已在編譯期間進入常量池的靜態欄位除外)。
  • 3、呼叫類的靜態方法。
  • 4、使用java.lang.reflect包下的方法對類進行反射呼叫的時候,如果類沒有初始化,則觸發初始化。
  • 5、初始化子類時,如果父類沒有初始化,則觸發初始化。
  • 6、JVM虛擬機器啟動被標明為啟動類的類(包含main()方法的類)。
  • 7、當使用JDK7新加入的動態語言支援時,如果一個java.langinvoke.MethodHandle例項最後的解析結果為REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四 種類型的方法控制代碼,並且這個方法控制代碼對應的類沒有進行過初始化,則需要先觸發其初始化。
  • 8、當一個介面中定義了JDK8新加入的預設方法(被default關鍵字修飾的介面方法)時,如果有這個介面的實現類發生了初始化,那該介面要在其之前被初始化。
public class ClassInitTest {
    static {
        System.out.println(
"啟動類初始化..."); } public static void main(String[] args) throws ClassNotFoundException { // 1. 建立例項 // InitTest1 it1 = new InitTest1(); // 2. 訪問靜態屬性 // System.out.println(InitTest1.n); // 3. 呼叫靜態方法 // InitTest1.method(); // 4. 反射 // Class cls = Class.forName("classloder.initialization.InitTest1");
// 5. 初始化子類 // InitTest2 it2 = new InitTest2(); // 6. 啟動類觸發初始化,執行main()觸發 // 7. 跳過 // 8. 當一個介面中的預設方法(被default關鍵字修飾的介面方法)初始化時,IfTestImpl初始化時會觸發InitTest1 it1 = new InitTest1(),證明IfTest介面初始化 // IfTest ifTest = new IfTestImpl(); } } // 類初始化會執行初始化類靜態屬性 class InitTest1 { static { System.out.println("InitTest1初始化..."); } public static int n = 10; // 靜態變數 public static void method() { // 靜態方法 n = 30; } } class InitTest2 extends InitTest1 { static { System.out.println("InitTest2初始化..."); } } // 介面,驗證第8條 interface IfTest { InitTest1 it1 = new InitTest1(); default void method() { } } class IfTestImpl implements IfTest { static { System.out.println("IfTestImpl初始化..."); } }

被動引用

除了上述主動引用的八種情況會觸發類的初始化,其他的引用都不會觸發初始化,被稱為被動引用,以下是被動引用的其中幾種情況舉例:

  • 1、通過子類的引用父類的靜態屬性,不會導致子類初始化。
  • 2、通過定義陣列引用類,不會導致該類初始化。
  • 3、訪問類中final修飾的靜態變數(常量在編譯期間會存入常量池,本質上並沒有直接引用到定義該常量的類,所以不會觸發該類的初始化)。
public class ClassInitTest2 {
    public static void main(String[] args) {
        // 1. 通過子類的引用父類的靜態屬性,不會導致子類初始化
//        System.out.println(Child.n);
        // 2. 通過定義陣列引用類,不會導致該類初始化。
//        Parent[] array = new Parent[10];
        // 3. 訪問類中final修飾的靜態變數
//        System.out.println(Parent.m);

    }
}

class Parent {
    static {
        System.out.println("Parent 初始化...");
    }
    public final static int m = 20;
    public static int n = 10;
}

class Child extends Parent {
    static {
        System.out.println("Child 初始化...");
    }
}