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

單例模式七種寫法深度解析:從餓漢到枚舉,哪種最適合你?

     編輯:Java 綜合教學

單例模式七種寫法深度解析:從餓漢到枚舉,哪種最適合你?

單例模式確保系統中僅存在一個類實例,節省資源並便於狀態維護。本文按演進順序剖析 7 種實現方式的優缺點、線程安全性與適用場景,助你精準選型。

一、 餓漢式(靜態常量)

public class Singleton {
    private Singleton() {}
    private static final Singleton INSTANCE = new Singleton();
    public static Singleton getInstance() { return INSTANCE; }
}
  • ✅ 簡單、類加載時初始化、天然線程安全。
  • ❌ 無延遲加載:即使從未調用 getInstance() 也會創建對象;構造函數耗時會拖慢類加載。

二、 懶漢式(同步方法)

private static Singleton instance;
public static synchronized Singleton getInstance() {
    if (instance == null) instance = new Singleton();
    return instance;
}
  • ✅ 延遲加載,線程安全。
  • ❌ synchronized 鎖住整個方法,高併發下效能極差(實測 5 線程 390ms vs 餓漢 0ms)。

三、 雙重檢查鎖 (DCL)

private static volatile Singleton instance;
public static Singleton getInstance() {
    if (instance == null) {
        synchronized (Singleton.class) {
            if (instance == null) instance = new Singleton();
        }
    }
    return instance;
}
  • ✅ 延遲加載,首次創建才加鎖,後續無鎖讀取。
  • ⚠️ JDK 1.5+ 必須加 volatile 禁止指令重排(先分配內存再初始化導致半初始化對象被可見)。
  • ❌ 代碼較繁瑣,閱讀理解成本高。

四、 靜態內部類(推薦 ⭐)

public class Singleton {
    private Singleton() {}
    private static class Holder {
        static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() { return Holder.INSTANCE; }
}
  • ✅ 延遲加載:外部類加載不初始化內部類,調用 getInstance() 時才觸發 Holder 初始化。
  • ✅ 線程安全:類初始化由 JVM 保證原子性,無需額外同步。
  • ✅ 代碼簡潔,無 volatile、無同步塊,效能最優。

五、 枚舉單例(Effective Java 強推 ⭐⭐)

public enum Singleton {
    INSTANCE;
    public void doSomething() { ... }
}
  • ✅ 天然線程安全、天然防反射攻擊(構造器私有化)、天然防序列化破壞(JVM 保證單例語義)。
  • ✅ 代碼最少,語義最清晰。
  • ❌ 不支援延遲加載(枚舉類加載時即創建實例);不支援繼承。

六、 容器管理單例(Spring 等 IOC 容器)

將類註冊為 @Component / @Bean,容器保證單例作用域。實際企業開發中**最常用**、最無感的方式。

七、 總結選型指南

場景推薦寫法理由
一般業務、服務端、無特殊延遲需求餓漢式 / 容器管理簡單、無鎖、效能最好
構造函數極其耗時、或可能永不使用靜態內部類延遲加載 + 零同步開銷
需防反射/序列化破壞、追求極致簡潔枚舉JVM 級保障
老舊 JDK (≤1.4) 環境同步方法 / DCL (無 volatile)歷史妥協

八、 單例的缺點別忘了

  1. 難以擴展:構造器私有,無法繼承。
  2. 職責易膨脹:為了「方便」塞入過多方法,違反單一職責原則。
  3. 測試困難:靜態方法難以 Mock,單元測試需借助 PowerMock 等工具。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved