類加載的時機

類加載的時機

類的生命週期

類從被加載到虛擬機內存開始,到卸載出內存爲止,它的整個生命週期包括如下 7 個階段:java

  • 加載
  • 驗證
  • 準備
  • 解析
  • 初始化
  • 使用
  • 卸載

驗證、準備、解析 3 個階段統稱爲鏈接。數組

Load Class

加載、驗證、準備、初始化和卸載這 5 個階段的順序是肯定的,類的加載過程必須按照這種順序循序漸進地開始(注意是「開始」,而不是「進行」或「完成」),而解析階段則不必定:它在某些狀況下能夠在初始化後再開始,這是爲了支持 Java 語言的運行時綁定。spa

類加載過程當中「初始化」開始的時機

Java 虛擬機規範沒有強制約束類加載過程的第一階段(即:加載)何時開始,但對於「初始化」階段,有着嚴格的規定。有且僅有 5 種狀況必須當即對類進行「初始化」:code

  • 在遇到 new、putstatic、getstatic、invokestatic 字節碼指令時,若是類還沒有初始化,則須要先觸發其初始化。
  • 對類進行反射調用時,若是類尚未初始化,則須要先觸發其初始化。
  • 初始化一個類時,若是其父類尚未初始化,則須要先初始化父類。
  • 虛擬機啓動時,用於須要指定一個包含 main() 方法的主類,虛擬機會先初始化這個主類。
  • 當使用 JDK 1.7 的動態語言支持時,若是一個 java.lang.invoke.MethodHandle 實例最後的解析結果爲 REF_getStatic、REF_putStatic、REF_invokeStatic 的方法句柄,而且這個方法句柄所對應的類還沒初始化,則須要先觸發其初始化。

這 5 種場景中的行爲稱爲對一個類進行主動引用,除此以外,其它全部引用類的方式都不會觸發初始化,稱爲被動引用blog

被動引用演示 Demo

Demo1

/**
 * 被動引用 Demo1:
 * 經過子類引用父類的靜態字段,不會致使子類初始化。
 * 
 * @author ylb
 *
 */
class SuperClass {
    static {
        System.out.println("SuperClass init!");
    }
    
    public static int value = 123;
}

class SubClass extends SuperClass {
    static {
        System.out.println("SubClass init!");
    }
}

public class NotInitialization {
    
    public static void main(String[] args) {
        System.out.println(SubClass.value);
        // SuperClass init!
    }
    
}

對於靜態字段,只有直接定義這個字段的類纔會被初始化,所以經過其子類來引用父類中定義的靜態字段,只會觸發父類的初始化而不會觸發子類的初始化。繼承

Demo2

/**
 * 被動引用 Demo2:
 * 經過數組定義來引用類,不會觸發此類的初始化。
 * 
 * @author ylb
 *
 */

public class NotInitialization {
    
    public static void main(String[] args) {
        SuperClass[] superClasses = new SuperClass[10];
    }
    
}

這段代碼不會觸發父類的初始化,但會觸發「[L 全類名」這個類的初始化,它由虛擬機自動生成,直接繼承自 java.lang.Object,建立動做由字節碼指令 newarray 觸發。接口

Demo3

/**
 * 被動引用 Demo3:
 * 常量在編譯階段會存入調用類的常量池中,本質上並無直接引用到定義常量的類,所以不會觸發定義常量的類的初始化。
 * 
 * @author ylb
 *
 */
class ConstClass {
    static {
        System.out.println("ConstClass init!");
    }
    
    public static final String HELLO_BINGO = "Hello Bingo";
    
}

public class NotInitialization {
    
    public static void main(String[] args) {
        System.out.println(ConstClass.HELLO_BINGO);
    }
    
}

編譯經過以後,常量存儲到 NotInitialization 類的常量池中,NotInitialization 的 Class 文件中並無 ConstClass 類的符號引用入口,這兩個類在編譯成 Class 以後就沒有任何聯繫了。生命週期

接口的加載過程

接口加載過程與類加載過程稍有不一樣。內存

當一個類在初始化時,要求其父類所有都已經初始化過了,可是一個接口在初始化時,並不要求其父接口所有都完成了初始化,當真正用到父接口的時候纔會初始化。rem

相關文章
相關標籤/搜索