## 簡介 單例模式是一種常用的軟體設計模式,用於創建類型。通過單例模式的方法創建的類在當前進程中只有一個實例。單例模式的類只能允許一個實例存在。單例模式的作用是保證在整個應用程式的生命周期中,任何一個時刻,單例類的實例都只存在一個。 組成部分: 1. 私有化構造方法。 2. 私有化內部實例。 3. ...
簡介
單例模式是一種常用的軟體設計模式,用於創建類型。通過單例模式的方法創建的類在當前進程中只有一個實例。單例模式的類只能允許一個實例存在。單例模式的作用是保證在整個應用程式的生命周期中,任何一個時刻,單例類的實例都只存在一個。
組成部分:
- 私有化構造方法。
- 私有化內部實例。
- 公有靜態方法用來獲取內部實例。
優缺點
單例模式的優點有:
- 提供了對唯一實例的受控訪問,可以保證對象的唯一性和一致性。
- 減少了記憶體開銷,避免了頻繁的創建和銷毀對象。
- 避免了對資源的多重占用,例如文件操作、資料庫連接等。
單例模式的缺點有:
- 不支持繼承和多態,違反了單一職責原則,一個類應該只關心內部邏輯,而不關心外部如何實例化。
- 不易擴展,如果需要創建多個實例,就需要修改代碼,違反了開閉原則,一個類應該對擴展開放,對修改關閉。
- 不支持有參數的構造函數,如果需要傳遞參數,就需要修改方法或者定義其他方法。
- 可能存在反射或者反序列化攻擊,破壞單例的唯一性。
應用場景
單例模式適用於以下場景:
- 需要頻繁創建和銷毀的對象,例如緩存、線程池、註冊表等。
- 需要控制資源的訪問,例如文件操作、資料庫連接等。
- 需要保證對象的唯一性和一致性,例如配置信息、全局變數等。
Java 代碼示例
在 Java 中,有五種不同的單例實現方法。其中包括餓漢式、懶漢式、雙檢鎖、靜態內部類和枚舉類。
單例模式的五種實現原理分別是餓漢式、懶漢式、雙重檢測、靜態內部類和枚舉類。它們各自的優缺點如下:
- 餓漢式:原理是在類載入的時候,就創建並初始化一個靜態的實例對象,然後通過一個靜態的方法返回這個實例。優點是線程安全,不需要加鎖;缺點是不支持延遲載入,可能會浪費資源。
public class Singleton {
private Singleton() {}
private static Singleton instance;
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 懶漢式:原理是在第一次調用獲取實例的方法時,才創建並初始化一個靜態的實例對象,然後返回這個實例。為了保證線程安全,需要給獲取實例的方法加上synchronized關鍵字。優點是支持延遲載入,節省資源;缺點是線程不安全,需要加鎖,影響性能。
public class Singleton {
private Singleton() {}
private static final Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
- 雙重檢測:原理是在第一次調用獲取實例的方法時,先判斷靜態的實例對象是否為空,如果為空,則進入同步代碼塊,再判斷一次是否為空,如果為空,則創建並初始化一個靜態的實例對象,然後返回這個實例。為了防止指令重排序導致空指針異常,需要給靜態的實例對象加上volatile關鍵字。優點是線程安全,支持延遲載入,不需要加鎖;缺點是可能會出現空指針異常,需要使用 volatile 關鍵字防止指令重排序。
public class Singleton {
private Singleton() {}
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
- 靜態內部類:原理是利用了 Java 靜態內部類的特性,即外部類載入時不會載入內部類,只有在使用到內部類時才會載入。因此,在第一次調用獲取實例的方法時,才會載入靜態內部類,並創建並初始化一個靜態的實例對象,然後返回這個實例。優點是線程安全,支持延遲載入,不需要加鎖;缺點是不能防止反射或者反序列化攻擊。
public class Singleton {
private Singleton() {}
private static class Instance {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return Instance.instance;
}
}
- 枚舉類:原理是利用了Java枚舉類型本身的特性,即枚舉類型在載入時就會創建所有的枚舉常量,並且保證了線程安全性和唯一性。因此,在調用獲取實例的方法時,直接返回枚舉常量即可。優點是線程安全,簡單易用,可以防止反射或者反序列化攻擊;缺點是不支持延遲載入,不能繼承其他類。
public enum Singleton {
INSTANCE;
}
這些不同的實現方式有不同的適用場景,需要根據具體的需求和條件來選擇。在這裡,我只能給出一些個人的看法,僅供參考。
- 如果對記憶體資源比較敏感,或者單例對象不需要頻繁使用,可以考慮使用懶漢式或者雙重檢測,因為它們支持延遲載入,可以節省資源。
- 如果對性能比較敏感,或者單例對象需要頻繁使用,可以考慮使用餓漢式或者靜態內部類,因為它們不需要加鎖,可以提高效率。
- 如果對安全性比較敏感,或者需要防止反射或者反序列化攻擊,可以考慮使用枚舉類,因為它可以保證實例的唯一性和不可變性。
- 如果對簡潔性比較敏感,或者不需要繼承其他類,可以考慮使用枚舉類,因為它是最簡單的實現方式。
個人來說在編碼效率和可維護性上我比較傾向於使用靜態內部類的實現方式,既能保證線程安全性,又能支持延遲載入。
Spring 代碼示例
在 Spring 框架中,Spring 預設使用單例模式來創建和管理 Bean 對象,但是可以通過 @Scope("singleton")
註解來指定 Bean 對象的作用域。
@Scope("singleton")
:表示該Bean對象是一個單例對象,在整個Spring容器中只有一個實例。@Scope("prototype")
:表示該Bean對象是一個原型對象,在每次請求時都會創建一個新的實例。@Scope("request")
:表示該Bean對象的作用域是一個HTTP請求,在同一個請求中只有一個實例。@Scope("session")
:表示該Bean對象的作用域是一個HTTP會話,在同一個會話中只有一個實例。
總結
單例模式是一種簡單而常用的設計模式,它可以保證一個類只有一個實例,並提供一個全局訪問點。單例模式有多種實現方式,各有優缺點。單例模式可以節約系統資源,避免資源衝突,保證對象的唯一性和一致性。但是單例模式也有不利於繼承和擴展的缺點,以及可能存在的安全隱患。在使用單例模式時,需要根據具體情況和需求選擇合適的方法,並註意避免潛在的問題。
關註公眾號【waynblog】每周分享技術乾貨、開源項目、實戰經驗、高效開發工具等,您的關註將是我的更新動力!