簡(jiǎn)介
單例模式是一種常用的軟件設(shè)計(jì)模式,用于創(chuàng)建類型。通過單例模式的方法創(chuàng)建的類在當(dāng)前進(jìn)程中只有一個(gè)實(shí)例。單例模式的類只能允許一個(gè)實(shí)例存在。單例模式的作用是保證在整個(gè)應(yīng)用程序的生命周期中,任何一個(gè)時(shí)刻,單例類的實(shí)例都只存在一個(gè)。
組成部分:
(資料圖片)
私有化構(gòu)造方法。私有化內(nèi)部實(shí)例。公有靜態(tài)方法用來獲取內(nèi)部實(shí)例。優(yōu)缺點(diǎn)
單例模式的優(yōu)點(diǎn)有:
提供了對(duì)唯一實(shí)例的受控訪問,可以保證對(duì)象的唯一性和一致性。減少了內(nèi)存開銷,避免了頻繁的創(chuàng)建和銷毀對(duì)象。避免了對(duì)資源的多重占用,例如文件操作、數(shù)據(jù)庫(kù)連接等。
單例模式的缺點(diǎn)有:
不支持繼承和多態(tài),違反了單一職責(zé)原則,一個(gè)類應(yīng)該只關(guān)心內(nèi)部邏輯,而不關(guān)心外部如何實(shí)例化。不易擴(kuò)展,如果需要?jiǎng)?chuàng)建多個(gè)實(shí)例,就需要修改代碼,違反了開閉原則,一個(gè)類應(yīng)該對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。不支持有參數(shù)的構(gòu)造函數(shù),如果需要傳遞參數(shù),就需要修改方法或者定義其他方法??赡艽嬖诜瓷浠蛘叻葱蛄谢簦茐膯卫奈ㄒ恍?。應(yīng)用場(chǎng)景
單例模式適用于以下場(chǎng)景:
需要頻繁創(chuàng)建和銷毀的對(duì)象,例如緩存、線程池、注冊(cè)表等。需要控制資源的訪問,例如文件操作、數(shù)據(jù)庫(kù)連接等。需要保證對(duì)象的唯一性和一致性,例如配置信息、全局變量等。Java 代碼示例
在 Java 中,有五種不同的單例實(shí)現(xiàn)方法。其中包括餓漢式、懶漢式、雙檢鎖、靜態(tài)內(nèi)部類和枚舉類。單例模式的五種實(shí)現(xiàn)原理分別是餓漢式、懶漢式、雙重檢測(cè)、靜態(tài)內(nèi)部類和枚舉類。它們各自的優(yōu)缺點(diǎn)如下:
餓漢式:原理是在類加載的時(shí)候,就創(chuàng)建并初始化一個(gè)靜態(tài)的實(shí)例對(duì)象,然后通過一個(gè)靜態(tài)的方法返回這個(gè)實(shí)例。優(yōu)點(diǎn)是線程安全,不需要加鎖;缺點(diǎn)是不支持延遲加載,可能會(huì)浪費(fèi)資源。
public class Singleton { private Singleton() {} private static Singleton instance; public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}
懶漢式:原理是在第一次調(diào)用獲取實(shí)例的方法時(shí),才創(chuàng)建并初始化一個(gè)靜態(tài)的實(shí)例對(duì)象,然后返回這個(gè)實(shí)例。為了保證線程安全,需要給獲取實(shí)例的方法加上synchronized關(guān)鍵字。優(yōu)點(diǎn)是支持延遲加載,節(jié)省資源;缺點(diǎn)是線程不安全,需要加鎖,影響性能。
public class Singleton { private Singleton() {} private static final Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; }}
雙重檢測(cè):原理是在第一次調(diào)用獲取實(shí)例的方法時(shí),先判斷靜態(tài)的實(shí)例對(duì)象是否為空,如果為空,則進(jìn)入同步代碼塊,再判斷一次是否為空,如果為空,則創(chuàng)建并初始化一個(gè)靜態(tài)的實(shí)例對(duì)象,然后返回這個(gè)實(shí)例。為了防止指令重排序?qū)е驴罩羔槷惓#枰o靜態(tài)的實(shí)例對(duì)象加上volatile關(guān)鍵字。優(yōu)點(diǎn)是線程安全,支持延遲加載,不需要加鎖;缺點(diǎn)是可能會(huì)出現(xiàn)空指針異常,需要使用 volatile 關(guān)鍵字防止指令重排序。
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; }}
靜態(tài)內(nèi)部類:原理是利用了 Java 靜態(tài)內(nèi)部類的特性,即外部類加載時(shí)不會(huì)加載內(nèi)部類,只有在使用到內(nèi)部類時(shí)才會(huì)加載。因此,在第一次調(diào)用獲取實(shí)例的方法時(shí),才會(huì)加載靜態(tài)內(nèi)部類,并創(chuàng)建并初始化一個(gè)靜態(tài)的實(shí)例對(duì)象,然后返回這個(gè)實(shí)例。優(yōu)點(diǎn)是線程安全,支持延遲加載,不需要加鎖;缺點(diǎn)是不能防止反射或者反序列化攻擊。
public class Singleton { private Singleton() {} private static class Instance { private static final Singleton instance = new Singleton(); } public static Singleton getInstance() { return Instance.instance; }}
枚舉類:原理是利用了Java枚舉類型本身的特性,即枚舉類型在加載時(shí)就會(huì)創(chuàng)建所有的枚舉常量,并且保證了線程安全性和唯一性。因此,在調(diào)用獲取實(shí)例的方法時(shí),直接返回枚舉常量即可。優(yōu)點(diǎn)是線程安全,簡(jiǎn)單易用,可以防止反射或者反序列化攻擊;缺點(diǎn)是不支持延遲加載,不能繼承其他類。
public enum Singleton { INSTANCE;}
這些不同的實(shí)現(xiàn)方式有不同的適用場(chǎng)景,需要根據(jù)具體的需求和條件來選擇。在這里,我只能給出一些個(gè)人的看法,僅供參考。
如果對(duì)內(nèi)存資源比較敏感,或者單例對(duì)象不需要頻繁使用,可以考慮使用懶漢式或者雙重檢測(cè),因?yàn)樗鼈冎С盅舆t加載,可以節(jié)省資源。如果對(duì)性能比較敏感,或者單例對(duì)象需要頻繁使用,可以考慮使用餓漢式或者靜態(tài)內(nèi)部類,因?yàn)樗鼈儾恍枰渔i,可以提高效率。如果對(duì)安全性比較敏感,或者需要防止反射或者反序列化攻擊,可以考慮使用枚舉類,因?yàn)樗梢员WC實(shí)例的唯一性和不可變性。如果對(duì)簡(jiǎn)潔性比較敏感,或者不需要繼承其他類,可以考慮使用枚舉類,因?yàn)樗亲詈?jiǎn)單的實(shí)現(xiàn)方式。
個(gè)人來說在編碼效率和可維護(hù)性上我比較傾向于使用靜態(tài)內(nèi)部類的實(shí)現(xiàn)方式,既能保證線程安全性,又能支持延遲加載。
Spring 代碼示例
在 Spring 框架中,Spring 默認(rèn)使用單例模式來創(chuàng)建和管理 Bean 對(duì)象,但是可以通過 @Scope("singleton")
注解來指定 Bean 對(duì)象的作用域。
@Scope("singleton")
:表示該Bean對(duì)象是一個(gè)單例對(duì)象,在整個(gè)Spring容器中只有一個(gè)實(shí)例。
@Scope("prototype")
:表示該Bean對(duì)象是一個(gè)原型對(duì)象,在每次請(qǐng)求時(shí)都會(huì)創(chuàng)建一個(gè)新的實(shí)例。
@Scope("request")
:表示該Bean對(duì)象的作用域是一個(gè)HTTP請(qǐng)求,在同一個(gè)請(qǐng)求中只有一個(gè)實(shí)例。
@Scope("session")
:表示該Bean對(duì)象的作用域是一個(gè)HTTP會(huì)話,在同一個(gè)會(huì)話中只有一個(gè)實(shí)例??偨Y(jié)
單例模式是一種簡(jiǎn)單而常用的設(shè)計(jì)模式,它可以保證一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)全局訪問點(diǎn)。單例模式有多種實(shí)現(xiàn)方式,各有優(yōu)缺點(diǎn)。單例模式可以節(jié)約系統(tǒng)資源,避免資源沖突,保證對(duì)象的唯一性和一致性。但是單例模式也有不利于繼承和擴(kuò)展的缺點(diǎn),以及可能存在的安全隱患。在使用單例模式時(shí),需要根據(jù)具體情況和需求選擇合適的方法,并注意避免潛在的問題。
關(guān)注公眾號(hào)【waynblog】每周分享技術(shù)干貨、開源項(xiàng)目、實(shí)戰(zhàn)經(jīng)驗(yàn)、高效開發(fā)工具等,您的關(guān)注將是我的更新動(dòng)力!
關(guān)鍵詞: