Java虛擬機規范中並沒有進行強制玉樹什么情況下需要開始類加載過程。但是對於初始化階段,虛擬機規范則是嚴格規定了有且僅有5種情況必須立即對類進行“初始化”(而加載,驗證,准備自然需要在此之前開始):
1.遇到new,getstatic,putstatic,或invokestatic這4條字節碼指令時,如果類沒有進行過初始化,則需要先觸發其初始化。生成這四條指令單最常見的Java代碼場景是:使用new關鍵字實例化對象的時候,讀取或設置一個類的靜態字段(被final修飾,已在變異期把結果放入常量池的靜態字段除外)的時候,以及調用一個類的靜態方法的時候。
2.使用java.lang.reflect包的方法對類進行反射調用的時候,如果類沒有進行過初始化,則需要先出法其初始化。
3.當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先出法其父類的初始化。
4.當虛擬機啟動時,用戶需要指定一個要執行的主類(包含main()方法的那個類),虛擬機會先初始化這個主類。
5.當使用JDK1.7的動態語言支持時,如果一個Java.lang.invoke.MethodHandle實例最后的解析結果REF_getStatic,REF_outStatic,REF_invokeStatic的方法句柄,並且這個方法句柄所對應的類沒有進行過初始化,則需要先觸發其初始化。
對於這五種會觸發類進行初始化的場景,虛擬機規范中是用來一個很強烈的現定於:“有且僅有”,這5種場景中的行為稱為一個類進行主動引用。
看下面的例子:
package logan.classload.study; public class SuperClass { static{ System.out.println("SuperClass init!"); } public static int value=123; }
package logan.classload.study; public class SubClass extends SuperClass { static{ System.out.println("SubClass init!"); } }
package logan.classload.study; public class NotInitialization { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(SubClass.value); } }

SuperClass init! 123
對於靜態字段,只有直接定義這個字段的類才會被初始化,因此通過其子類來引用父類中定義的靜態字段,只會觸發父類的初始化而不會觸發子類的初始化。至於是否要觸發子類的加載和驗證,在虛擬機規范中並未明確規定。這點取決於虛擬機的具體實現。對於Sun HotSpot來說,可通過-XX:+TraceClassLoading參數觀察到此操作會導致子類的加載。
看下面一個例子:
package logan.classload.study; public class NotInitialization { public static void main(String[] args) { // TODO Auto-generated method stub SuperClass[]sca = new SuperClass[10]; } }

沒有輸出
運行之后發現沒有輸出,“SuperClass init!”說明沒有處罰類SuperClass的初始化。但是這段代碼里面出發了另外一個名為“[Llogan.classload.study.SuperClass”的類的初始化階段,對於用戶代碼來說這並不是一個合法的類名稱,他是一個由虛擬機自動生成的,直接繼承與java.lang.Object的子類,創建動作由字節碼指令newarray觸發。
這個類代表了一個元素類型為logan.classload.SuperClass的一位數組,數組中應有的屬性和方法(用戶可以直接使用的只有被修飾為public的length屬性和clone()方法)都是現在這個類里。
看下面一個例子
package logan.classload.study; public class ConstClass { static{ System.out.println("ConstClass init!"); } public static final String HelloWorld = "hello world"; }
package logan.classload.study; public class NotInitialization { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(ConstClass.HelloWorld); } }

hello world
輸出里面沒有輸出“ConstClass init!”,這是因為雖然在Java源碼中引用了ConstClass類中放常量HelloWorld,但是其實在變異階段通過常量傳播優化,已經將此常量的值“hello world”存儲到NotInitialization類的常量池中,以后NotInitialization對常量ConstClass.HelloWorld的引用實際都被轉化為NotInitialization類對自身常量池的引用了。
也就是說,實際上NotInitialization的Class文件之中並沒有ConstClass類的符號引用入口,這兩個類在編譯成Class之后就不存在任何聯系了。
接口的加載過程與類加載過程稍微有一些不同,針對接口需要做一下特殊說明:接口也有初始化過程,這點與類類是一致的。上面的代碼都是用靜態語句塊“static{}”來輸出初始化信息的,而接口中不能使用“static{}”語句塊,但是編譯器仍然會為接口生成“<client>()”類構造器,用於初始化接口中多定義的成員變量。接口與類真正有所區別的是前面講述的5種“有且僅有”需要開始初始化場景中的第三種:當一個類在初始化時,要求其父類全部都已經初始化過了,但是一個接口在初始化時,並不要求其父類接口全部完成初始化,只有在真正使用到父類接口的時候(如引用接口中定義的常量)才會初始化。