單例模式確保類別全域僅有一個實例,並提供全域存取點。在 Java/C# 中,靜態內部類 (Static Nested Class) 寫法結合了延遲初始化、線程安全、程式碼簡潔 三大優勢,是公認最佳實踐之一 (Effective Java Item 3)。
public final class Singleton {
// 私有建構子,防止外部實例化
private Singleton() {}
// 靜態內部類持有唯一實例
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
// 全域存取點
public static Singleton getInstance() {
return Holder.INSTANCE;
}
// 業務方法
public void doSomething() {
System.out.println("Singleton doing work...");
}
}
public sealed class Singleton
{
// 私有建構子
private Singleton() {}
// 靜態內部類 (巢狀類別)
private static class Holder
{
// .NET 保證靜態建構子只執行一次,且線程安全
internal static readonly Singleton Instance = new Singleton();
}
public static Singleton Instance => Holder.Instance;
public void DoSomething() => Console.WriteLine("Singleton working...");
}
| 特性 | 靜態內部類寫法 | 雙重檢查鎖定 (DCL) | 急切初始化 |
|---|---|---|---|
| 線程安全 | ✅ 類別載入機制天然保證 | ✅ 需 volatile + 鎖定 | ✅ 但無延遲 |
| 延遲初始化 | ✅ 首次呼叫 getInstance() 才載入 Holder | ✅ | ❌ 類別載入即建立 |
| 效能 | ✅ 無同步開銷,JIT 內聯優化 | ⚠️ volatile 讀取開銷 | ✅ 最快但無延遲 |
| 程式碼簡潔度 | ✅ 極簡 | ❌ 複雜易錯 | ✅ 簡單但無延遲 |
| 序列化安全 | ⚠️ 需實作 readResolve | ⚠️ 同上 | ⚠️ 同上 |
| 反射攻擊 | ⚠️ 可用 Enum 單例防禦 | ⚠️ 同上 | ⚠️ 同上 |
Holder 類別只有在呼叫 getInstance() 存取 Holder.INSTANCE 時才會被載入初始化。final (Java) / readonly (C#) 確保實例不可重新指派。
// Java: 防反射
private Singleton() {
if (Holder.INSTANCE != null) {
throw new IllegalStateException("Already initialized");
}
}
// Java: 防序列化
private Object readResolve() {
return Holder.INSTANCE;
}
// C#: 防反射 (較難完全防禦,可拋出例外)
private Singleton() {
if (Holder.Instance != null) throw new InvalidOperationException("Singleton already created");
}
// C#: 序列化建議標記 [Serializable] 並實作 ISerializable 或用 NonSerialized
public enum Singleton { INSTANCE; } — 絕對防反射、防序列化、線程安全、簡潔。首選若不需繼承/延遲初始化。靜態內部類單例 是延遲初始化、線程安全、高效能、程式碼極簡的完美平衡。Java/C# 通用,強烈建議作為預設選擇。