23種設(shè)計(jì)模式(1) java單例模式
23種設(shè)計(jì)模式第四篇:java單例模式
定義:
單例模式,是一種常用的軟件設(shè)計(jì)模式。在它的核心結(jié)構(gòu)中只包含一個(gè)被稱(chēng)為單例的特殊類(lèi)。通過(guò)單例模式可以保證系統(tǒng)中一個(gè)類(lèi)只有一個(gè)實(shí)例。即一個(gè)類(lèi)只有一個(gè)對(duì)象實(shí)例。
特點(diǎn):
1、單例類(lèi)只能有一個(gè)實(shí)例。
2、單例類(lèi)必須自己自己創(chuàng)建自己的唯一實(shí)例。
3、單例類(lèi)必須給所有其他對(duì)象提供這一實(shí)例
單例模式的要點(diǎn):
1、私有的構(gòu)造方法
2、指向自己實(shí)例的私有靜態(tài)引用
3、以自己實(shí)例為返回值的靜態(tài)的公有的方法
單例模式根據(jù)實(shí)例化對(duì)象時(shí)機(jī)的不同分為兩種:
一種是餓漢式單例,一種是懶漢式單例。
餓漢式單例在單例類(lèi)被加載時(shí)候,就實(shí)例化一個(gè)對(duì)象交給自己的引用;而懶漢式在調(diào)用取得實(shí)例方法的時(shí)候才會(huì)實(shí)例化對(duì)象。
代碼如下:
餓漢式單例
public class Singleton { private static Singleton singleton = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return singleton; } }
懶漢式單例
public class Singleton { private static Singleton singleton; private Singleton(){} public static synchronized Singleton getInstance(){ if(singleton==null){ singleton = new Singleton(); } return singleton; } }
單例模式還有一種比較常見(jiàn)的形式:雙重鎖的形式
public class Singleton{ private static volatile Singleton instance=null; private Singleton(){ //do something } public static Singleton getInstance(){ if(instance==null){ synchronized(SingletonClass.class){ if(instance==null){ instance=new Singleton(); } } } return instance; } }
這個(gè)模式將同步內(nèi)容下方到if內(nèi)部,提高了執(zhí)行的效率,不必每次獲取對(duì)象時(shí)都進(jìn)行同步,只有第一次才同步,創(chuàng)建了以后就沒(méi)必要了。
這種模式中雙重判斷加同步的方式,比第一個(gè)例子中的效率大大提升,因?yàn)槿绻麊螌觟f判斷,在服務(wù)器允許的情況下,假設(shè)有一百個(gè)線程,耗費(fèi)的時(shí)間為100*(同步判斷時(shí)間+if判斷時(shí)間),而如果雙重if判斷,100的線程可以同時(shí)if判斷,理論消耗的時(shí)間只有一個(gè)if判斷的時(shí)間。
所以如果面對(duì)高并發(fā)的情況,而且采用的是懶漢模式,最好的選擇就是雙重判斷加同步的方式。
單例模式的優(yōu)點(diǎn):
1、在內(nèi)存中只有一個(gè)對(duì)象,節(jié)省內(nèi)存空間。
2、避免頻繁的創(chuàng)建銷(xiāo)毀對(duì)象,可以提高性能。
3、避免對(duì)共享資源的多重占用。
4、可以全局訪問(wèn)。
單例模式的優(yōu)點(diǎn):
1、擴(kuò)展困難,由于getInstance靜態(tài)函數(shù)沒(méi)有辦法生成子類(lèi)的實(shí)例。如果要拓展,只有重寫(xiě)那個(gè)類(lèi)。
2、隱式使用引起類(lèi)結(jié)構(gòu)不清晰。
3、導(dǎo)致程序內(nèi)存泄露的問(wèn)題。
適用場(chǎng)景:
由于單例模式的以上優(yōu)點(diǎn),所以是編程中用的比較多的一種設(shè)計(jì)模式。以下為使用單例模式的場(chǎng)景:
1、需要頻繁實(shí)例化然后銷(xiāo)毀的對(duì)象。
2、創(chuàng)建對(duì)象時(shí)耗時(shí)過(guò)多或者耗資源過(guò)多,但又經(jīng)常用到的對(duì)象。
3、資源共享的情況下,避免由于資源操作時(shí)導(dǎo)致的性能或損耗等
4、控制資源的情況下,方便資源之間的互相通信。
單例模式注意事項(xiàng):
只能使用單例類(lèi)提供的方法得到單例對(duì)象,不要使用反射,否則將會(huì)實(shí)例化一個(gè)新對(duì)象。
不要做斷開(kāi)單例類(lèi)對(duì)象與類(lèi)中靜態(tài)引用的危險(xiǎn)操作。
多線程使用單例使用共享資源時(shí),注意線程安全問(wèn)題。
關(guān)于Java中單例模式的一些常見(jiàn)問(wèn)題:
單例模式的對(duì)象長(zhǎng)時(shí)間不用會(huì)被jvm垃圾收集器收集嗎
除非人為地?cái)嚅_(kāi)單例中靜態(tài)引用到單例對(duì)象的聯(lián)接,否則jvm垃圾收集器是不會(huì)回收單例對(duì)象的。
jvm卸載類(lèi)的判定條件如下:
1,該類(lèi)所有的實(shí)例都已經(jīng)被回收,也就是java堆中不存在該類(lèi)的任何實(shí)例。
2,加載該類(lèi)的ClassLoader已經(jīng)被回收。
3,該類(lèi)對(duì)應(yīng)的java.lang.Class對(duì)象沒(méi)有任何地方被引用,無(wú)法在任何地方通過(guò)反射訪問(wèn)該類(lèi)的方法。
只有三個(gè)條件都滿足,jvm才會(huì)在垃圾收集的時(shí)候卸載類(lèi)。顯然,單例的類(lèi)不滿足條件一,因此單例類(lèi)也不會(huì)被回收。
在一個(gè)jvm中會(huì)出現(xiàn)多個(gè)單例嗎
在分布式系統(tǒng)、多個(gè)類(lèi)加載器、以及序列化的的情況下,會(huì)產(chǎn)生多個(gè)單例,這一點(diǎn)是無(wú)庸置疑的。那么在同一個(gè)jvm中,會(huì)不會(huì)產(chǎn)生單例呢?使用單例提供的getInstance()方法只能得到同一個(gè)單例,除非是使用反射方式,將會(huì)得到新的單例。
代碼如下:
Class c = Class.forName(Singleton.class.getName()); Constructor ct = c.getDeclaredConstructor(); ct.setAccessible(true); Singleton singleton = (Singleton)ct.newInstance();
這樣,每次運(yùn)行都會(huì)產(chǎn)生新的單例對(duì)象。所以運(yùn)用單例模式時(shí),一定注意不要使用反射產(chǎn)生新的單例對(duì)象。
在getInstance()方法上同步有優(yōu)勢(shì)還是僅同步必要的塊更優(yōu)優(yōu)勢(shì)?
因?yàn)殒i定僅僅在創(chuàng)建實(shí)例時(shí)才有意義,然后其他時(shí)候?qū)嵗齼H僅是只讀訪問(wèn)的,因此只同步必要的塊的性能更優(yōu),并且是更好的選擇。
缺點(diǎn):只有在第一次調(diào)用的時(shí)候,才會(huì)出現(xiàn)生成2個(gè)對(duì)象,才必須要求同步。而一旦singleton 不為null,系統(tǒng)依舊花費(fèi)同步鎖開(kāi)銷(xiāo),有點(diǎn)得不償失。
單例類(lèi)可以被繼承嗎
根據(jù)單例實(shí)例構(gòu)造的時(shí)機(jī)和方式不同,單例模式還可以分成幾種。但對(duì)于這種通過(guò)私有化構(gòu)造函數(shù),靜態(tài)方法提供實(shí)例的單例類(lèi)而言,是不支持繼承的。
這種模式的單例實(shí)現(xiàn)要求每個(gè)具體的單例類(lèi)自身來(lái)維護(hù)單例實(shí)例和限制多個(gè)實(shí)例的生成。但可以采用另外一種實(shí)現(xiàn)單例的思路:登記式單例,來(lái)使得單例對(duì)繼承開(kāi)放。
轉(zhuǎn)自:java知音
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
解決Unable to start embedded container&nbs
這篇文章主要介紹了解決Unable to start embedded container SpringBoot啟動(dòng)報(bào)錯(cuò)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07教你如何使用Java8實(shí)現(xiàn)菜單樹(shù)形數(shù)據(jù)
今天給大家?guī)?lái)的是關(guān)于JAVA的相關(guān)知識(shí),文中圍繞著如何使用Java8實(shí)現(xiàn)菜單樹(shù)形數(shù)據(jù)展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06Spring的@Value注入復(fù)雜類(lèi)型(通過(guò)@value注入自定義類(lèi)型)
Spring的@Value可以注入復(fù)雜類(lèi)型嗎?今天教你通過(guò)@value注入自定義類(lèi)型。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12Java實(shí)現(xiàn)簡(jiǎn)單的飛機(jī)大戰(zhàn)游戲(控制主飛機(jī)篇)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單的飛機(jī)大戰(zhàn)游戲,控制主飛機(jī),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05詳解Kotlin:forEach也能break和continue
這篇文章主要介紹了詳解Kotlin:forEach也能break和continue的相關(guān)資料,需要的朋友可以參考下2017-06-06String StringBuilder StringBuffer區(qū)別以及源碼分析
string是C++標(biāo)準(zhǔn)庫(kù)的一個(gè)重要的部分,主要用于字符串處理??梢允褂幂斎胼敵隽鞣绞街苯舆M(jìn)行string操作,同時(shí),C++的算法庫(kù)對(duì)string類(lèi)也有著很好的支持,并且string類(lèi)還和c語(yǔ)言的字符串之間有著良好的接口2021-06-06Spring security基于數(shù)據(jù)庫(kù)中賬戶密碼認(rèn)證
這篇文章主要介紹了Spring security基于數(shù)據(jù)庫(kù)中賬戶密碼認(rèn)證,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03SpringBoot實(shí)現(xiàn)緩存組件配置動(dòng)態(tài)切換的步驟詳解
現(xiàn)在有多個(gè)springboot項(xiàng)目,但是不同的項(xiàng)目中使用的緩存組件是不一樣的,有的項(xiàng)目使用redis,有的項(xiàng)目使用ctgcache,現(xiàn)在需要用同一套代碼通過(guò)配置開(kāi)關(guān),在不同的項(xiàng)目中切換這兩種緩存,本文介紹了SpringBoot實(shí)現(xiàn)緩存組件配置動(dòng)態(tài)切換的步驟,需要的朋友可以參考下2024-07-07