2013-05-27 229 views
18

這是一個非常廣泛的傳播枚舉單碼:通過枚舉方式的單例是懶惰初始化的?

public enum enumClazz{ 
    INSTANCE 
    enumClazz(){ 
    //do something 
    } 
} 

和一堆地說,這是一個懶惰的初始化。

Java虛擬機規範給出實現的類和接口加載的時間 靈活性和鏈接, 但嚴格:一個類型的生命週期 - 但在我讀的「Inside the Java Virtual Machine」第7章我很困惑定義了初始化的時間。所有實現 必須初始化每個類或接口的第一次活動使用。所述 以下六個情況資格作爲活性的用途:

  • 創建類的一個新實例(在字節碼,一個新的指令的執行可替換地,經由隱式的創建, 反射,克隆,或反序列化。)。
  • 由類聲明的靜態方法的調用(在字節碼,一個invokestatic指令的執行)
  • 通過一個類或接口中聲明的靜態域的使用或分配,除了靜態字段是最終並由 初始化一個編譯時常量表達式(在字節碼中S,A getstatic或putstatic指令)的執行
  • 的Java API中的某些反射的方法,如在類類方法或類在java.lang.reflect中調用 包
  • 初始化一類的子類的(一類的初始化需要其超類的現有的初始化。)
  • 一類的指定爲初始類(與主()<法)時,Java虛擬機啓動

第三點粗體顯示,如果字段爲static final,則該字段的初始化在編譯時發生。同樣,INSTANCEenumClazz隱含地等於public static final並符合第三點。

如果我的理解錯誤,有人能糾正我嗎? 「

回答

25

enum實例字段是而不是」通過編譯時常量表達式初始化「。他們 不能,因爲only String and primitive types are possible types for a compile-time constant expression

這意味着當第一次訪問INSTANCE(這正是期望的效果)時,該類將被初始化。

在粗體文本的異常以上存在,因爲這些常量(具有一個編譯時間常量表達式初始化static final字段)將有效地在編譯期間內聯:

class A { 
    public static final String FOO = "foo"; 

    static { 
    System.out.println("initializing A"); 
    } 
} 

class B { 
    public static void main(String[] args) { 
    System.out.println(A.FOO); 
    } 
} 

執行類B在這個例子將不是初始化A(並且將不是打印「初始化A」)。如果您查看爲B生成的字節碼,您將看到一個字符串文本,其值爲「foo」,no對類A的引用。

3

具有大膽風格的第三點澄清,如果字段爲「靜態最後的」,現場的initialzation是發生在complie時間

不完全是 - 它僅適用於「靜這是最後的和編譯時初始化常量表達式「字段:

static final String = "abc"; //compile time constant 
static final Object = new Object(); //initialised at runtime 

在你的情況下,單會被初始化d當枚舉類被加載時,即第一次在您的代碼中引用enumClazz

因此,它是有效的懶人,除非你有一個說法在某處你像這樣的代碼,例如,這將不必要地初始化你單身的類加載過程的一部分:

Class<?> c = enumClazz.class; 
+0

類辛格爾頓{公共靜態最終instance = new Singleton(); ...}那麼,對於泛型類的方式,這個「實例」是否被惰性初始化? –

+0

是的,它會以相同的方式懶惰地初始化。 – assylias

+0

全部清除。感謝一堆:) –