제이온

[Effective Java] 項目 3. 使用私有建構函式或枚舉類型來確保單例

  • 撰写语言: 韓国語
  • 基准国家: 所有国家country-flag
  • 信息技术

撰写: 2024-04-27

撰写: 2024-04-27 00:48

單例模式

單例模式的概念

單例模式指的是只能建立一個實例的類別。單例模式的典型範例包括無狀態物件或唯一的系統元件。但是,單例類別如果沒有將類型定義為介面並以其實作來定義,則難以進行測試。


建立單例模式的方法

使用 public static 且為 final 欄位的成員的方式



private 建構函式僅在初始化 Elvis 實例時呼叫一次,確保整個系統中只有一個實例。但是,可以使用 AccessibleObject.setAccessible() 呼叫 private 建構函式,這種透過反射修改的方法可以在建立第二個物件時丟出例外來阻止。


  • 優點
    • API 明確顯示該類別為單例。
    • 簡潔。


提供 public static 的靜態工廠方法的方式


除了透過反射修改之外,此方法也能確保整個系統中只有一個實例。只是將欄位改為 private,並使用靜態工廠方法來返回物件。


  • 優點
    • 可以在不更改 API 的情況下,將單例模式改為非單例模式。
      • 例如,可以讓靜態工廠方法為每個呼叫它的線程返回不同的實例。
    • 如果需要,可以更改為泛型單例工廠方法。
    • 可以使用靜態工廠方法的函式參考作為供應商。
      • 例如,可以使用 Supplier<Elvis> 代替 Elvis::getInstance。


如果不需要利用上述優點,建議使用第一種方法。


使用列舉類型的方式


最理想的方式是使用列舉類型。與前兩種方法相比,它對反射攻擊更安全,代碼也更簡潔。此外,如下所述,前兩種方法在序列化時需要添加額外的代碼,這是一個缺點。

但是,需要注意的是,雖然要建立的單例可以繼承介面,但不能繼承類別。


序列化單例類別時需要注意的事項

如果要序列化使用第一種或第二種方法建立的單例類別,除了簡單地實現 Serializable 之外,還需要將所有實例欄位宣告為 transient,並重新定義並提供 readResolve() 方法。



資料來源

评论0