單例模式確保 JVM 進程中僅存在一個實例,帶來雙重收益:
代價:無抽象層難擴展、職責過重違背單一職責、濫用導致資源爭用、序列化/反射/類加載器可能破壞單一性。
public class Singleton1 {
private Singleton1() {}
private static final Singleton1 INSTANCE = new Singleton1();
public static Singleton1 getInstance() { return INSTANCE; }
}
✅ 線程安全、調用快 ❌ 無延遲加載、類加載即佔用內存
public class Singleton2 {
private Singleton2() {}
private static Singleton2 instance;
public static Singleton2 getInstance() {
if (instance == null) instance = new Singleton2(); // 非線程安全
return instance;
}
}
✅ 延遲加載 ❌ 多線程下會創建多實例
public class Singleton3 {
private Singleton3() {}
private static volatile Singleton3 instance;
public static Singleton3 getInstance() {
if (instance == null) {
synchronized (Singleton3.class) {
if (instance == null) instance = new Singleton3();
}
}
return instance;
}
}
✅ 延遲+線程安全+高性能 ❌ 代碼較繁瑣、首次調用稍慢
public class Singleton4 {
private Singleton4() {}
private static class Holder {
static final Singleton4 INSTANCE = new Singleton4();
}
public static Singleton4 getInstance() { return Holder.INSTANCE; }
}
利用類加載機制天然線程安全,Holder 類僅在首次調用 getInstance() 時加載,完美平衡所有指標。
public enum Singleton5 {
INSTANCE;
public void doSomething() { /* 业务逻辑 */ }
}
✅ 防反射、防序列化破壞、語法最簡 ❌ 無延遲加載、不支持懶加載邏輯
public class SingletonManager {
private static final Map CONTAINER = new ConcurrentHashMap<>();
public static void register(String key, Object instance) {
CONTAINER.putIfAbsent(key, instance);
}
public static Object get(String key) { return CONTAINER.get(key); }
}
適用於管理多類型單例,統一獲取入口,降低耦合,框架級應用首選。
| 場景 | 原理 | 對策 |
|---|---|---|
| 反射調用私有構造器 | Constructor.setAccessible(true) 強行實例化 | 構造器中檢查 if (INSTANCE != null) throw Exception 或改用枚舉 |
| 多類加載器 | 不同 ClassLoader 加載同一類產生多個 Class 對象 | 指定統一類加載器(如線程上下文類加載器) |
| 序列化/反序列化 | 反序列化默認創建新對象 | 實現 readResolve() 返回唯一實例 |
// 破壞前
ObjectInputStream.readObject() → newSingleton != singleton (false)
// 修復:添加 readResolve()
private Object readResolve() { return INSTANCE; }
// 修復後
singleton == newSingleton (true)