單例模式在Java中用于確保一個類只有一個實例并提供全局訪問點,適用于控制資源創建與訪問,如數據庫連接池、配置管理器等。其核心優勢在于避免資源浪費和保證狀態一致性,但濫用會導致代碼耦合度高、測試困難。實現方式包括:1. 基礎懶加載實現,線程不安全;2. 加 synchronized 關鍵字實現線程安全但性能較差;3. 雙重檢查鎖定,需加 volatile 避免指令重排序,兼顧性能與安全;4. 靜態內部類實現,推薦使用,線程安全且支持懶加載;5. 枚舉方式,簡潔可靠,防止反射與反序列化破壞單例。實際開發中需注意:spring框架默認bean為單例,無需手動實現;避免過度依賴單例影響擴展性;考慮是否真的需要單例,部分場景可用依賴注入替代。
單例模式在Java中是一種常見的設計模式,用于確保一個類只有一個實例,并提供全局訪問點。雖然實現方式多種多樣,但不同場景下適用的最佳實踐也有所不同。
為什么使用單例模式?
單例的核心目的是控制資源的創建和訪問,比如數據庫連接池、配置管理器、日志記錄器等。它能避免重復初始化帶來的資源浪費,也能保證狀態一致性。不過要注意的是,濫用單例可能導致代碼耦合度高、測試困難等問題。
單例模式的基本結構
最簡單的單例實現包括:
立即學習“Java免費學習筆記(深入)”;
- 私有構造函數(防止外部 new)
- 靜態私有實例
- 公共靜態方法返回該實例
public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
這種方式是懶加載的,但在多線程環境下不安全。多個線程同時調用 getInstance() 可能導致創建多個實例。
如何實現線程安全的單例?
為了保證線程安全,常見做法有幾種:
- 加 synchronized 關鍵字
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
缺點是性能較差,每次調用都要同步。
- 雙重檢查鎖定(double-Checked Locking)
private volatile static Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }
加上 volatile 是為了避免指令重排序問題,這是關鍵細節之一。
- 靜態內部類實現
public class Singleton { private Singleton() {} private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } }
這種寫法兼顧了線程安全和懶加載,推薦使用。
枚舉方式:最簡潔的單例實現
Java 的枚舉類型天然支持單例特性,還能防止反射攻擊和反序列化破壞單例。
public enum SingletonEnum { INSTANCE; public void doSomething() { // 業務邏輯 } }
使用時直接調用 SingletonEnum.INSTANCE.doSomething();,非常簡潔可靠。如果你不需要懶加載,這是一個非常推薦的方式。
實際開發中的注意事項
- 如果你用的是 spring 框架,默認的 Bean 作用域就是單例的,不需要自己實現。
- 避免將單例與全局變量混為一談,過度依賴單例會讓系統難以擴展和維護。
- 考慮是否真的需要單例,有些場景可以用依賴注入替代。
基本上就這些。單例看似簡單,但要真正用好還是有不少細節需要注意。