核心前提:系統確實需要某個類別「全域僅一個實例」。若類別可共存多個實例,則不應強行套用單例。以下兩個常見誤區:
將原本屬於不同領域物件的狀態硬塞進單例,造成錯誤的耦合與依賴。正確做法:將狀態歸屬其所描述的實體類別,單例僅管理真正的共享資源(如印表機佇列、配置檔)。
資料庫連線本質上可有多個實例並存,不屬於「單一實例」需求。若為解決連線池問題,應使用專門的連線池框架,而非單例包裝單一 Connection 並在 finalize() 釋放——這不可靠且違背單例意圖。
| 類型 | 特徵 | 典型用途 |
|---|---|---|
| 有狀態 | 可變,持有資料(如序號產生器、快取) | 資源倉庫、狀態儲存 |
| 無狀態 | 不可變,僅提供工具方法 | 工具類、策略演算法 |
無狀態單例等同於不可變模式,天然執行緒安全,適合分散式環境。
EJB、RMI、JINI 等技術下,單例侷限於單一 JVM。若有狀態單例跨 JVM 被序列化或遠端呼叫,每個 JVM 都會擁有獨立實例,導致狀態不一致(如訂單號重複)。鐵律:分散式系統嚴禁使用有狀態單例。
同一 JVM 內,不同類別載入器載入同一單例類別會產生多個 Class 物件,進而產生多個單例實例。常見於多 Servlet 引擎共存的 J2EE 伺服器。若單例有狀態,內網與外網使用者將看到不同狀態。解決方案:採用無狀態單例,或引入外部協調機制(如資料庫、分散式快取)。