類從被加載到虛擬機內存開始,到卸載出內存爲止,它的整個生命週期包括如下 7 個階段:java
驗證、準備、解析 3 個階段統稱爲鏈接。數組
加載、驗證、準備、初始化和卸載這 5 個階段的順序是肯定的,類的加載過程必須按照這種順序循序漸進地開始(注意是「開始」,而不是「進行」或「完成」),而解析階段則不必定:它在某些狀況下能夠在初始化後再開始,這是爲了支持 Java 語言的運行時綁定。spa
Java 虛擬機規範沒有強制約束類加載過程的第一階段(即:加載)何時開始,但對於「初始化」階段,有着嚴格的規定。有且僅有 5 種狀況必須當即對類進行「初始化」:code
這 5 種場景中的行爲稱爲對一個類進行主動引用,除此以外,其它全部引用類的方式都不會觸發初始化,稱爲被動引用。blog
/** * 被動引用 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: * 經過數組定義來引用類,不會觸發此類的初始化。 * * @author ylb * */ public class NotInitialization { public static void main(String[] args) { SuperClass[] superClasses = new SuperClass[10]; } }
這段代碼不會觸發父類的初始化,但會觸發「[L 全類名」這個類的初始化,它由虛擬機自動生成,直接繼承自 java.lang.Object,建立動做由字節碼指令 newarray 觸發。接口
/** * 被動引用 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