類聲明週期
類從被加載到虛擬機內存開始,到卸載出內存爲止,他生命週期包括了:
加載->驗證->準備->解析->初始化->使用->卸載
什麼情況下開始類加載第一階段?
- 遇到new,getstatic,putstatic或者invokestatic這4條字節碼指令時。如果類沒有驚醒過初始化,則先觸發其初始化。常見場景:new實例化對象,讀取或者設置一個類的靜態字段(被final修飾,已在編譯期間把結果放入常量池的靜態字段除外)的時候,以及調用靜態方法。
- 使用java.lang.reflect包的方法對類進行反射調用的時候,如果類沒有進行初始化,則需先觸發初始化。
- 當初始化一個類,如果發現其父類還沒有進行過初始化,則先出法其父類的初始化。
當虛擬機啓動時,用戶需要指定一個需要執行的主類,虛擬機會初始化這個主類。
這四種場景的行爲成爲對一個類的主動引用,除了以上所用引用類的方式都不會觸發初始化,稱爲被動引用。
被動引用示例1:
package classloading;
public class SuperClass {
static {
System.out.println("classloading.SuperClass init!");
}
public static int value = 123;
}
package classloading;
public class SubClass extends SuperClass {
static {
System.out.println("subclass init!");
}
}
package classloading;
public class NotInitialization {
public static void main(String[] args) {
System.out.println(SubClass.value);
}
}
輸出結果:
classloading.SuperClass init!
123
對於靜態字段只有直接定義這個字段的類纔會被初始化,因此通過子類引用父類定義的靜態字段並不會觸發子類初始化。至於是否要觸發取決於虛擬機的具體實現。
被動引用示例2:
package classloading;
public class NotInitialization {
public static void main(String[] args) {
SuperClass[] sca=new SuperClass[10];
}
}
運行後發現並沒有觸發任何子類或者父類初始化。
被動引用示例3:
package classloading;
public class ConstantClass {
static {
System.out.println("ConstantClass init!!");
}
public static final String HELLOWORLD = "hello world!";
}
package classloading;
public class NotInitialization {
public static void main(String[] args) {
System.out.println(ConstantClass.HELLOWORLD);
}
}
運行後發現並未打印出ConstantClass init!!,這是因爲java源碼在編譯階段將次常量的值存儲到了NotInitialization的常量池中,對常量ConstantClass.HELLOWORLD的引用實際上傳化爲了對自身常量池的引用了。
接口初始化:
接口與類初始化區別:類在書初始化的時候需要其父類全部初始化,但是接口在初始化時候,並不需要父接口全部完成初始化,只有真正使用到父類接口的時候纔會初始化。