您最近深入研究了單例設計模式、如何在 Java 中實現它以及它的用途。但是,如果我告訴您 Java 自帶開箱即用的單例呢?感興趣嗎?那麼讓我們開始吧。

您可能已經了解Enum 類。它有一個你應該知道的特殊功能。具體來說,Enum實現了單例設計模式。此選項幾乎與涉及公共字段的單例方法相同。

單例作為枚舉:


public enum Device {   
    PRINTER	
} 
    

單例作為公共變量:


public class Printer {   
    public static final Printer PRINTER = new Printer();   
    private Printer() {
    }
//…
}
    

枚舉方法比公共字段方法更緊湊,因為我們不需要編寫自己的實現最重要的是,枚舉在序列化方面沒有問題。

枚舉的序列化與普通對像不同:只有枚舉名稱的值被序列化。在反序列化期間,該方法與反序列化名稱一起使用以獲取實例。此外,枚舉可以保護您免受反射攻擊

您將在第二個模塊的課程中了解有關反射的更多信息,我們將在其中探索反射 API

Java 禁止實例化枚舉——這是Constructor類的newInstance方法實現中的一個限制,在通過反射創建對象時通常會調用該方法。

Constructor.newInstance中的代碼摘錄。用於創建 枚舉


if ((clazz.getModifiers() & Modifier.ENUM) != 0)
    throw new IllegalArgumentException("Cannot reflectively create enum objects");
    

使用枚舉 創建單例的缺點包括:

  • 缺少惰性初始化,因為對像是立即創建的,初始化不能延遲。

  • 其他類不能擴展。也就是說,在需要繼承另一個類的情況下,將枚舉用作單例是行不通。在這種情況下,我們需要轉向我們已經熟悉的其他實現選項:靜態方法或公共變量。

  • 將枚舉作為單例使用時,只能使用一個枚舉字段。


public enum Device extends Electricity { 
    PRINTER 
}
    

這段代碼會給我們一個編譯錯誤:

枚舉不允許擴展子句

但是如果我們需要實現一個接口,就沒有問題,因為枚舉可以實現接口:


public enum Device implements Electricity { 
    PRINTER 
}
    

如果不需要使用繼承,最好通過enum實現單例模式。我們並不是唯一推薦這一點的人——Joshua Bloch 本人也這樣做

這種實現方法為您提供方便、緊湊、開箱即用的序列化、防止反射攻擊和唯一性——優秀的單例所需要的一切!