new。public class HungrySingleton {
private static final HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton() {}
public static HungrySingleton getInstance() { return INSTANCE; }
}
✅ 簡單、無鎖、效能最佳。
⚠️ 類載入即佔用記憶體,若實例極大且未必用到則浪費。
public class LazySingleton {
private static volatile LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) { // 第一次檢查(無鎖)
synchronized (LazySingleton.class) { // 鎖定類物件
if (instance == null) { // 第二次檢查(雙重檢查鎖 DCL)
instance = new LazySingleton();
}
}
}
return instance;
}
}
✅ 延遲載入、節省資源。
⚠️ 需 volatile 禁止指令重排序,否則 DCL 失效。
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2); // true
| 陷阱 | 後果 | 防禦方案 |
|---|---|---|
| 反射呼叫私有建構子 | 產生第二個實例 | 建構子內判斷 if (INSTANCE != null) throw new IllegalStateException() |
| 序列化/反序列化 | 產生新實例 | 實作 readResolve() 回傳現有實例 |
| 類載入器隔離 | 不同 ClassLoader 各產生一個 | 指定統一 ClassLoader 或使用枚舉單例 |
public enum EnumSingleton {
INSTANCE;
public void doSomething() { ... }
}
✅ 天然防反射、防序列化、防類載入器隔離、程式碼最簡潔。
⚠️ 不支援延遲載入(類載入即建立),大多數場景可接受。
餓漢式優先、需要延遲載入用 DCL、追求極致防禦用枚舉。切記:單例 ≠ 全域變數,請謹慎評估耦合度與可測試性。