標籤:java類的載入時機 java類的初始化 被動引用和主動引用 jvm類載入 class初始化
Java虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校正、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java類型,這就是虛擬機器的載入機制。
類從被載入到虛擬機器記憶體中開始,到卸載出記憶體為止,它的整個生命週期包括了:載入(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(using)、和卸載(Unloading)七個階段。其中驗證、準備和解析三個部分統稱為串連(Linking),如下如所示。
這七個階段,載入、驗證、準備、初始化和卸載這五個階段的順序是確定的,類的載入過程必須按照這個順序來按部就班地開始,而解析階段則不一定,它在某些情況下可以在初始化階段後再開始。
類的生命週期的每一個階段通常都是互相交叉混合式進行的,通常會在一個階段執行的過程中調用或啟用另外一個階段。
Java虛擬機器規範沒有強制性約束在什麼時候開始類載入過程,但是對於初始化階段,虛擬機器規範則嚴格規定了有且只有四種情況必需立即對類進行“初始化”(而載入、驗證、準備階段則必需在此之前開始),這四種情況歸類如下:
1.遇到new、getstatic、putstatic或invokestatic這4條位元組碼指令時,如果類沒有進行過初始化,則需要先觸發其初始化。產生這4條指令最常見的Java代碼情境是:使用new關鍵字執行個體化對象時、讀取或者設定一個類的靜態欄位(被final修飾、已在編譯器把結果放入常量池的靜態欄位除外)時、以及調用一個類的靜態方法的時候。
2.使用java.lang.reflect包的方法對類進行反射調用的時候,如果類沒有進行過初始化,則需要先觸發其初始化。
3.當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要觸發父類的初始化。
4.當虛擬機器啟動時,使用者需要指定一個執行的主類(包含main()方法的類),虛擬機器會先初始化這個類。
對於這四種觸發類進行初始化的情境,在java虛擬機器規範中限定了“有且只有”這四種情境會觸發。這四種情境的行為稱為對類的主動引用,除此以外的所有引用類的方式都不會觸發類的初始化,稱為被動引用。
下面通過三個執行個體來說明被動引用:
樣本1:
父類SuperClass.java public class SuperClass { static{ System.out.println("SuperClass init!"); } public static int value = 123; } 子類SubClass.java public class SubClass extends SuperClass { static{ System.out.println("SubClass init!"); } } 主類NotInitialization.javapublic class NotInitialization { public static void main(String[] args) { System.out.println(SubClass.value); } }
輸出結果:SuperClass init! 123
由結果可以看出只輸出了“SuperClass init!”,沒有輸出“SubClass init!”。這是因為對於靜態欄位,只有直接定義該欄位的類才會被初始化,因此當我們通過子類來引用父類中定義的靜態欄位時,只會觸發父類的初始化,而不會觸發子類的初始化。
樣本2:
SuperClass[ ] scs=new SuperClass[11];
如上,當初始化一個對象數組的時候,也不會觸發類的初始化。
樣本3:
public class ConstClass { static { system.out.printl("const");} public static final int age =123; }public class NotInitialization{ public static void main(String[ ] args){ system.out.println(ConstClass.age);}
此時並不會出現 “const”,因為在NotInitialization類在編譯的時候已經把ConstClass中的變數age放在常量池中了,訪問時直接取出age即可,不會引發ConstClass的初始化。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
JVM中java類的載入時機