首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 單例模式完整指南:餓漢、懶漢、登記式三大實現與結構解析

     編輯:Java 相關知識

單例模式核心三要素

  1. 某個類別只能有一個實例。
  2. 必須自行建立這個實例。
  3. 必須自行向整個系統提供這個實例。

所有客戶端共享同一單例物件,單例物件內部持有對自身的參考。

單例模式物件圖

資源管理視角的單例應用

  • 外部資源:印表機佇列、傳真卡、通訊埠——避免並發衝突。
  • 內部資源:配置檔、訪客計數器、錯誤日誌、效能監控——集中管理,避免多頭馬車。

經典範例:Windows 回收站

整個視窗系統僅一個回收站實例,自行提供存取點,完美契合單例定義。

Windows 回收站

Java 中三種經典單例實現

1. 餓漢式單例 —— 推薦預設選擇

類別載入時靜態初始化實例,天然執行緒安全,程式碼最簡潔。

public final class EagerSingleton {
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    private EagerSingleton() {}
    public static EagerSingleton getInstance() { return INSTANCE; }
}

優點:無同步開銷、實作簡單、類別載入機制保證安全。缺點:若實例建構耗時且從未使用,會浪費資源(但在 Java 中靜態初始化順序固定,比 C++ 更適合)。

餓漢式類別圖

2. 懶漢式單例 —— 延遲初始化

首次呼叫 getInstance() 時才建立實例,需處理多執行緒同步。

public final class LazySingleton {
    private static LazySingleton instance = null;
    private LazySingleton() {}
    public static synchronized LazySingleton getInstance() {
        if (instance == null) instance = new LazySingleton();
        return instance;
    }
}

優點:按需建立。缺點:synchronized 導致每次呼叫皆同步,效能較差;若建構過程耗時,首次併發衝突機率大。

懶漢式類別圖

警告:切勿嘗試「雙重檢查鎖定」優化,在 Java 中不可靠,詳見後續章節附錄。

3. 登記式單例 —— 支援繼承的單例

GoF 提出,透過登錄表映射類別名稱到實例,允許子類別繼承並各自成為單例。父類別建構子為 protected,子類別建構子為 public(此為缺點,允許繞過登錄表直接實例化)。

public class RegSingleton {
    private static final Map REGISTRY = new HashMap<>();
    static {
        RegSingleton self = new RegSingleton();
        REGISTRY.put(self.getClass().getName(), self);
    }
    protected RegSingleton() {}
    public static RegSingleton getInstance(String name) {
        if (name == null) name = RegSingleton.class.getName();
        if (!REGISTRY.containsKey(name)) {
            try {
                REGISTRY.put(name, (RegSingleton) Class.forName(name).newInstance());
            } catch (Exception e) { /* handle */ }
        }
        return REGISTRY.get(name);
    }
    public String about() { return "Hello, I am RegSingleton."; }
}

子類別範例:

public class RegSingletonChild extends RegSingleton {
    public RegSingletonChild() {}
    public static RegSingletonChild getInstance() {
        return (RegSingletonChild) RegSingleton.getInstance(RegSingletonChild.class.getName());
    }
    public String about() { return "Hello, I am RegSingletonChild."; }
}

登記式單例類別圖 登記式單例子類別圖

登記式單例缺點總結

  • 子類別建構子必須公開,破壞封裝。
  • 父類別實例必須存在才能建立子類別實例,可能造成浪費。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved