單例模式確保類全局唯一實例,並提供全局訪問點。看似簡單,實則暗藏線程安全、反射破壞、序列化重建、類加載器隔離等深坑。
| 模式 | 線程安全 | 懶加載 | 性能 | 推薦度 |
|---|---|---|---|---|
| 餓漢式 (static final) | ✅ | ❌ | 極高 | ⭐⭐⭐ (常用) |
| 懶漢式 + synchronized | ✅ | ✅ | 低 (鎖粗) | ⭐ |
| 雙重檢查鎖 (DCL) + volatile | ✅ | ✅ | 高 | ⭐⭐ |
| 靜態內部類 | ✅ | ✅ | 高 | ⭐⭐⭐⭐⭐ (最推薦) |
| 枚舉單例 | ✅ | ❌ | 高 | ⭐⭐⭐⭐⭐ (防反射/序列化) |
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
利用類加載機制保證線程安全與懶加載,無鎖、代碼簡潔、JVM 原生支持。
public enum Singleton {
INSTANCE;
public void doSomething() { ... }
}
天然防反射攻擊 (構造器私有化最終由 JVM 強制)、天然防序列化重建 (JVM 保證枚舉常量唯一)、代碼最少。
| 破壞手段 | 原理 | 防御 |
|---|---|---|
反射 setAccessible(true) | 暴力調用私有構造器 | 構造器中檢查 if (INSTANCE != null) throw Exception;或用枚舉 |
| 反序列化 | readResolve() 返回新對象 | 實現 readResolve() 返回 INSTANCE;或用枚舉 |
| 多類加載器 | 不同 ClassLoader 加載同類視為不同類 | 指定類加載器;OSGi/模塊化環境需謹慎設計 |
Spring @Scope("singleton")、@Component 默認單例,由容器管理生命周期、依賴注入、AOP 代理,**遠優於手寫單例**。業務代碼優先使用框架單例,工具庫無框架依賴時才考慮枚舉/內部類。