JVM中java類的加載時機
來自: http://blog.csdn.net//chenleixing/article/details/47099725
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.java public 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的初始化。