對類的使用方式分為:主動使用、被動使用
所有的java虛擬機實作必須在每個類或介面被java程式“首次主動使用”時才初始化他們
ps:
被動使用不會初始化類,但是有可能會加載類(JVM規范里沒有說明)并且,如果在加載的程序中,遇到了.class檔案的缺失或者存在錯誤,類加載器只會在首次主動使用它們時才會報錯,如果一直沒有主動使用,則不會報錯
主動使用:
創建類的實體、訪問某個類或者介面的非final型別的靜態變數/對該靜態變數賦值、呼叫類的靜態方法、反射、初始化一個類的子類、jvm啟動時被標明為啟動類的類(如Java Test、main方法所在的類)、jdk1.7開始提供動態語言的支持相關(很少使用)
ps:
初始化一個類的子類這條規則,不適用于介面,即:初始化一個類的子類,會先初始化它的父類,但是不一定會初始化它的介面,只有當首次使用該介面的靜態變數時,才會初始化,
被動使用:
除了主動使用的其他使用方式
(1)通過子類參考父類的靜態欄位,為子類的被動使用,不會導致子類初始化
class Dfather{ static int count = 1; static{ System.out.println("Initialize class Dfather"); } } class Dson extends Dfather{ static{ System.out.println("Initialize class Dson"); } } public class Test4 { public static void main(String[] args) { int x = Dson.count; } }
上面這個例子中,雖然是以Dson.count 形式呼叫的,但是因為count是Dfather的靜態成員變數,所以只初始化Dfather類,而不初始化Dson類
(2)通過陣列定義類參考類,為類的被動使用,不會觸發此類的初始化
原因:其實陣列已經不是E型別了,E的陣列jvm在運行期,會動態生成一個新的型別,新型別為:
如果是一維陣列,則為:[L+元素的類全名;二維陣列,則為[[L+元素的類全名
如果是基礎型別(int/float等),則為[I(int型別)、[F(float型別)等
class E{ static{ System.out.println("Initialize class E"); } } public class Test5 { public static void main(String[] args) { E[] e = new E[10]; } }
(3)常量在編譯階段會存入呼叫方法所在的類的常量池中(這個例子存在F類的常量池中),本質上沒有直接參考到定義常量的類,因此不會觸發定義常量的類的初始化
在本例子中,編譯好了Test6后,類Test6和類F就完全沒有關系了,甚至,你可以把類F的class檔案刪掉,也不影響程式的正常執行
class F{ static final int count = 1; static{ System.out.println("Initialize class F"); } } public class Test6 { public static void main(String[] args) { int x = F.count; } }
如上例中,F類中定義的count是final物件,其在編譯階段就會存入呼叫類的常量池中
但是,如果把代碼改成
class F{ static final String s = UUID.randomUUID().toString(); static{ System.out.println("Initialize class F"); } } public class Test6 { public static void main(String[] args) { String x = F.s; } }
則陳述句 "Initialize class F" 會列印出來,因為UUID.randomUUID().toString()這個方法,是運行期確認的,所以,這不是被動使用
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/53095.html
標籤:Java
