java-android設(shè)計模式學(xué)習(xí)筆記(1):單例模式-編程開發(fā)技術(shù)_第1頁
java-android設(shè)計模式學(xué)習(xí)筆記(1):單例模式-編程開發(fā)技術(shù)_第2頁
java-android設(shè)計模式學(xué)習(xí)筆記(1):單例模式-編程開發(fā)技術(shù)_第3頁
java-android設(shè)計模式學(xué)習(xí)筆記(1):單例模式-編程開發(fā)技術(shù)_第4頁
java-android設(shè)計模式學(xué)習(xí)筆記(1):單例模式-編程開發(fā)技術(shù)_第5頁
已閱讀5頁,還剩5頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、java/android設(shè)計模式學(xué)習(xí)筆記(1):單例模式-編程開發(fā)技術(shù)java/android設(shè)計模式學(xué)習(xí)筆記(1): 單例模式原文岀處:shawn_dut前段時間公司一些同事在討論單例模式(我是最渣的一個,都插不上嘴t_t ), 這個模式使用的頻率很高,也可能是很多人最熟悉的設(shè)計模式,當(dāng)然單例模式也 算是最簡單的設(shè)計模式之一吧,簡單歸簡單,但是在實(shí)際使用的吋候也會有一些 坑。ps:對技術(shù)感興趣的同鞋加群544645972 一起交流。設(shè)計模式總目錄java/android設(shè)計模式學(xué)習(xí)筆記口錄特點(diǎn)確保某一個類只有一個實(shí)例,而且自行實(shí)例化并向整個系統(tǒng)提供這個實(shí)例。 單例模式的使用很廣泛,比如:線程

2、池(threadpool)、緩存(cache)、對話 框、處理偏好設(shè)置、和注冊表(registry)的對象、日志對象,充當(dāng)打印機(jī)、顯 卡等設(shè)備的驅(qū)動程序的對象等,這些類的對象只能有一個實(shí)例,如果制造出多個 實(shí)例,就會導(dǎo)致很多問題的產(chǎn)生,程序的行為異常,資源使用過量,或者不一致 的結(jié)果等,所以單例模式最主要的特點(diǎn):1. 構(gòu)造函數(shù)不對外開放,一般為private;2. 通過一個靜態(tài)方法或者枚舉返回單例類對象;3. 確保單例類的對彖有且只有一個,尤其是在多線程的壞境下;4. 確保單例類對象在反序列化時不會重新構(gòu)建對象。通過將單例類構(gòu)造函數(shù)私冇化,使得客戶端不能通過new的形式手動構(gòu)造單例 類的對彖。

3、單例類會暴露一個共有靜態(tài)方法,客戶端需要條用這個靜態(tài)方法獲取 到單-例類的唯一對象,在獲取到這個單例對象的過程屮需要確保線程安全,即在 多線程環(huán)境下構(gòu)造單例類的對象也是冇且只冇一個,這是單例模式較關(guān)鍵的一個 地方。o主要優(yōu)點(diǎn)單例模式的主要優(yōu)點(diǎn)如下:1. 單例模式提供了對唯一實(shí)例的受控訪問。因?yàn)閱卫惙庋b了它的唯一實(shí)例, 所以它可以嚴(yán)格控制客戶怎樣以及何時訪問它。2. 山于在系統(tǒng)內(nèi)存中只存在一個対象,因此可以節(jié)約系統(tǒng)資源,對于一些需要 頻繁創(chuàng)建和銷毀的對彖單例模式無疑可以提鬲系統(tǒng)的性能。3. 允許可變數(shù)目的實(shí)例?;趩卫J轿覀兛梢赃M(jìn)行擴(kuò)展,使用與單例控制相 似的方法來獲得指定個數(shù)的對象實(shí)例,既

4、節(jié)省系統(tǒng)資源,乂解決了單例對象 共享過多有損性能的問題。主要缺點(diǎn)1. 由于單例模式中沒有抽彖層,因此單例類的擴(kuò)展有很大的困難。2. 單例類的職責(zé)過重,在一定程度上違背了“單一職責(zé)原則二因?yàn)閱卫惣瘸?當(dāng)了工廠角色,提供了工廠方法,同時乂充當(dāng)了產(chǎn)品角色,包含一些業(yè)務(wù)方 法,將產(chǎn)品的創(chuàng)建和產(chǎn)品的本身的功能融合到一起。3. 現(xiàn)在很多面向?qū)﹀枵Z言java. c#)的運(yùn)行環(huán)境都提供了自動垃圾回收的技 術(shù),因此,如果實(shí)例化的共亨對象長時間不被利用,系統(tǒng)會認(rèn)為它是垃圾, 會自動銷毀并冋收資源,下次利用時又將重新實(shí)例化,這將導(dǎo)致共享的單例 對象狀態(tài)的丟火。4. 單例對象如果持有context,那么很容易引發(fā)內(nèi)

5、存泄漏,此時需耍注意傳遞 給單例對象的context最好是application contextouml類圖類圖很簡單,singleton類有一個static的instance對象,類型為singlcton ,構(gòu)造函數(shù)為private,提供一個getlnstancc()的靜態(tài)函數(shù),返 回剛才的instance對象,在該函數(shù)屮進(jìn)行初始化操作。示例與源碼單例模式的寫法很多,總結(jié)一下:lazy initialization, thread-unsafety (懶漢法, 線程不安全)延遲初始化,一般很多人稱為懶漢法,寫法一目了然,在需耍使用的時候去調(diào)用 getlnstanceo函數(shù)去獲取singlet

6、on的唯一靜態(tài)對象,如果為空,就會去做一 個額外的初始化操作。public class singleton private static singleton instanee = null;private singleton () public static singleton getlnstance() if (instance 二二 null) instance = new singleton();return instanee;需要注意的是這種寫法在多線程操作屮是不安全的,后果是可能會產(chǎn)生多個 singleton對象,比如兩個線程同吋執(zhí)行g(shù)etlnstanceo函數(shù)ii寸,然后同吋執(zhí)行

7、到new操作時,最后很有可能會創(chuàng)建兩個不同的對象。lazy initialization, thread-safety,double-checked (懶漢法,線程安全)需要做到線程安全,就需要確保任意時刻只能有口僅有一個線程能夠執(zhí)行new singleton對象的操作,所以可以在getlnstance()函數(shù)上加上synchronized 關(guān)鍵字,類似于:public static synchronized singleton getlnstance() if(singleton 二二 null)instance = new singleton();return instancc;但是套用h

8、ead first上的一句話,對于絕大部分不需要同步的情況來說, synchronized會讓函數(shù)執(zhí)彳亍效率耕糕一百倍以上(since synchronizing a method could in some extreme cases decrease performance by a factor of 100 or higher),所以就有了 double-checked (雙重檢測)的方法:public class singleton private volatile static singleton instance 二 null;private singleton() public

9、 static singleton getlnstance() if (instance = null) synchronized (singleton, class)if (instance 二二 null) instanee = new singleton(); rcturn instancc;我們假設(shè)兩個線程a, b同時執(zhí)行到了 gettnstance ()這個方法,第一個if判斷, 兩個線程同時為true,進(jìn)入if語句,里面有個synchronized同步,所以之后 有且僅有一個線程a會執(zhí)彳亍到synchronized語句內(nèi)部,接著再次判斷instance 是否為空,為空就去new s

10、ingleton對象并且賦值給instance, a線程退岀 synchronized語句,交出同步鎖,b線程進(jìn)入synchronized語句內(nèi)部,if判 斷instance是否為空,防止創(chuàng)建不同的instance對象,這也是第二個if判斷 的作用,b線程發(fā)現(xiàn)不為空,所以直接退出,所以最終a和b線程可以獲取到同 一個singleton對象,z后的線程調(diào)用gctlnstancc ()函數(shù),都會因?yàn)閕nstance 不為空而直接返回,不會受到synchronized的性能影響。volatile關(guān)鍵字介紹double-checked方法用到了 volatile關(guān)鍵字,volatile關(guān)鍵字的作用需要

11、仔細(xì) 介紹一下,在c/c+中,volatile關(guān)鍵字的作用和java中是不一樣的,總結(jié)一 下:1. c/c+中的volatile關(guān)鍵字作用可見性“可見性”指的是在一個線程屮對該變量的修改會馬上由工作內(nèi) 存(work memory)寫回主內(nèi)存(main memory),所以會馬上反應(yīng) 在其它線程的讀取操作中。順便一提,工作內(nèi)存和主內(nèi)存可以近似 理解為實(shí)際電腦屮的高速緩存和主存,工作內(nèi)存是線程獨(dú)享的,主 存是線程共享的。不可優(yōu)化性“不可優(yōu)化”特性,volatile告訴編譯器,不要對我這個變量進(jìn) 行各種激進(jìn)的優(yōu)化,甚至將變量直接消除,保證程序員寫在代碼小 的指令,一定會被執(zhí)行。順序性”順序性”,能夠

12、保證volatile變量間的順序性,編譯器不會進(jìn) 行亂序優(yōu)化。volatile變量與非volatile變量的順序,編譯器不 保證順序,可能會進(jìn)行亂序優(yōu)化。同時,c/c+ volatile關(guān)鍵詞, 并不能用于構(gòu)建happens-before語義,因此在進(jìn)行多線程程序設(shè) 計時,要小心使用volatile,不要掉入volatile變量的使用陷阱 之屮。2. java中volatile關(guān)鍵字作用java也支持volatile關(guān)鍵字,但它被用于其他不同的用途。當(dāng)volatile用于 一個作用域時,jqvei保證如卜:(適用于java所有版本)讀和寫一個volatile變量有全局的排序。 也就是說每個線程

13、訪問一個volatile作川域時會在繼續(xù)執(zhí)行z前讀取 它的當(dāng)前值,而不是(可能)使用一個緩存的值。(但是并不保證經(jīng) 常讀寫volatile作用域吋讀和寫的相對順序,也就是說通常這并不是 冇用的線程構(gòu)建)。(適用于java5及其z后的版本)volatile的讀和寫建立了一個 happens-before關(guān)系,類似于申請和釋放一個互斥鎖8。使用volatile會比使用鎖更快,但是在一些情況下它不能工作。volatile使用 范圍在java5中得到了擴(kuò)展,特別是雙重檢查鎖定現(xiàn)在能夠正確工作9。上面有一個細(xì)節(jié),j3v3 5版木z后volatile的讀與寫才建立了一個 happens-before的關(guān)系

14、,之前的版本會出現(xiàn)一個問題:why is volatile used in this example of double checked locking,這個答案寫的很清楚了,線程 a 在 完全構(gòu)造完instance對象之前就會給instance分配內(nèi)存,線程b在看到 instance已經(jīng)分配了內(nèi)存不為空就回去使用它,所以這就造成了 b線程使用了 部分初始化的instance對象,最后就會出問題了。double-checked locking 里而冇一句話as of j2se 5. 0, this problem has been fixed. the volatile keyword now

15、 ensures that multiple threads handie the singleton instanee correctly. this new idiom is described in 2 and 3.所以對于android來說,使用volatile關(guān)鍵字是一點(diǎn)問題都沒有的了。參考文章:volatile 變量c/c+ volatile關(guān)鍵詞深度剖析java中volatile的作用以及用法eager initialization thread-safety (餓漢法, 線程安全)“餓漢法”就是在使用該變量之/而就將該變量進(jìn)行初始化,這當(dāng)然也就是線程安 全的了,寫法也很簡單:p

16、rivate static singleton instance 二 new singleton();private singleton()name = "eager initialization thread-safety 1;public static singleton getlnstance()return instance;或者private static singleton instance = null;private singleton()name 二"eager initialization thread-safety 2;static instance

17、= new singleton();publ ic singleton getlnstance()return instancc;代碼都很簡單,一個是直接進(jìn)行初始化,另一個是使用靜態(tài)塊進(jìn)行初始化,目的 都是一個:在該類進(jìn)行加載的時候就會初始化該對象,而不管是否需耍該對象。 這么寫的好處是編寫簡單,而口是線程安全的,但是這時候初始化instance顯 然沒有達(dá)到lazy loading的效果。static inner class thread-safety(靜態(tài)內(nèi)部類,線程安全)由于在走陽中,靜態(tài)內(nèi)部類是在使用中初始化的,所以可以利用這個天生的延 遲加載特性,去實(shí)現(xiàn)一個簡單,延遲加載,線程安全的

18、單例模式:private static class singletonholderprivate static final singleton instance = new singleton();privatc singleton()name = ,zstatic inner class thread-safet5zz;public static singleton getlnstance()return singletonholder, instanee;定義一個singlctonholdcr的靜態(tài)內(nèi)部類,在該類小定義一個外部類 singleton的靜態(tài)對象,并且直接初始化,在外部類sin

19、gleton的 getlnstance()方法中直接返冋該對象。由于靜態(tài)內(nèi)部類的使用是延遲加載機(jī) 制,所以只有當(dāng)線程調(diào)用到gettnstanceo方法時才會去加載 singletonllolder類,加載這個類的時候又會去初始化instance變量,所以這 個就實(shí)現(xiàn)了延遲加載機(jī)制,同時也只會初始化這一次,所以也是線程安全的,寫 法也很簡單。ps上面提到的所有實(shí)現(xiàn)方式都有兩個共同的缺點(diǎn): 都需要額外的i 作(serializable > transient > read resol ve():實(shí)現(xiàn)序列化,否則每次反 序列化一個序列化的對象實(shí)例吋都會創(chuàng)建一個新的實(shí)例??赡軙腥耸褂梅瓷?/p>

20、強(qiáng)行調(diào)用我們的私有構(gòu)造器(如果要避免這種情況,可以修改 構(gòu)造器,讓它在創(chuàng)建第二個實(shí)例的時候拋異常)。enum (枚舉寫法)jdk1. 5 z后加入enum特性,可以使用enum來實(shí)現(xiàn)單例模式:enum singleenuminstance(,zenum singleton thread-safety);private string name;singleenum(string name)this, name 二 name;public string getname()return name;使用枚舉除了線程安全和防止反射強(qiáng)行調(diào)用構(gòu)造器z外,還提供了 口動序列化機(jī) 制,防止反序列化的時候創(chuàng)建新的

21、對象。因此,effective java推薦盡可能地 使用枚舉來實(shí)現(xiàn)單例。但是很不幸的是android屮并不推薦使用enum ,主要 是因?yàn)樵趈ava屮枚舉都是繼承自java. lang. enum類,首次調(diào)用時,這個類會 調(diào)用初始化方法來準(zhǔn)備毎個枚舉變量。毎個枚舉項都會被聲明成一個靜態(tài)變量, 并被賦值。在實(shí)際使用吋會有點(diǎn)問題,這是google的官方文檔介紹:enums often require more than twice as much memory as static constants. you should strictly avoid using cnums on androi

22、d這篇博客也專門計算了 enum的大?。汉鷦P-the price of enums,所以枚舉寫 法的缺點(diǎn)也就很明顯了。登記式單例實(shí)際上維護(hù)了一組單例類的實(shí)例,將這些實(shí)例存放在一個map (登記 薄)中,對于已經(jīng)登記過的實(shí)例,則從map直接返回,對于沒有登記的,則先登 記,然后返回。類似spring里面的方法,將類名注冊,下次從里面直接獲取。public class singleton private static map<string, singleton> map = new ilashmap<string,singlcton> ();static singleton

23、 single = new singleton();map. put (single. getclass (). getname (), single);保護(hù)的默認(rèn)構(gòu)造了 protected singleton()/靜態(tài)工廠方法,返還此類惟一的實(shí)例public static singleton getlnstanee(string name) if (name 二二 nul1) namc 二 singleton, class. gctnamc ();system, o ut print in ("name = null+>name=,+name);if (map. get(na

24、me)二二 null) map. put(namc, (singlcton) class. forname(name) newlnstance(); catch (instantiationexception e) e. printstacktrace (); catch (t1legalaccessexception e) c. printstacktracco ; catch (classnotfoundexception e) e. printstacktrace ();return map. get(namc);一個示意性的商業(yè)方法 public string about () ret

25、urn "hello, i am regsingleton.;public static void meiin( str in g args) singleton single3 = singleton, getlnstance(null); system out. println(single3. about ();這種方式我極少見到,另外其實(shí)內(nèi)部實(shí)現(xiàn)述是用的餓漢式單例,因?yàn)槠湫〉?static方法塊,它的單例在類被裝載的時候就被實(shí)例化了??偨Y(jié)綜上所述,平時在android中使用double-checked或者singletonholder都 是口j以的,畢竟android早就不使

26、用jdk5之詢的版本了。由于android中的 多進(jìn)程機(jī)制,在不同進(jìn)程中無法創(chuàng)建同一個instance變量,就像application 類會初始化兩次一樣,這點(diǎn)需要注意。但是不管采取何種方案,請時刻牢記單例的三大要點(diǎn):線程安全; 延遲加載;序列化與反序列化安全。單例模式同時也冇缺點(diǎn):單例模式一般沒有接口,擴(kuò)展很困難,若要擴(kuò)展,除了修改代碼基本上沒有笫二種 途徑可以實(shí)現(xiàn);單例對象如果持有context,那么很容易引發(fā)內(nèi)存淞漏,此時需要注意傳遞給單例 對象的 context 最好為 application contexto創(chuàng)建型模式?rules of thumb有些吋候創(chuàng)建型模式是可以重疊使用的,

27、有i些抽象工廠模式和原型模式都可以 使用的場景,這個時候使用任一設(shè)計模式都是合理的;在其他情況下,他們各自 作為彼此的補(bǔ)充:抽象工廠模式可能會使用一些原型類來克隆并且返回產(chǎn)品對 彖。抽彖工廠模式,建造者模式和原型模式都能使用單例模式來實(shí)現(xiàn)他們自己;抽象 工廠模式經(jīng)常也是通過工廠方法模式實(shí)現(xiàn)的,但是他們都能夠使用原型模式來實(shí) 現(xiàn);通備情況卜,設(shè)計模式剛開始會使用工廠方法模式(結(jié)構(gòu)清晰,更容易定制化, 子類的數(shù)量爆炸),如呆設(shè)計者發(fā)現(xiàn)需要更多的靈活性時,就會慢慢地發(fā)展為抽 象工廠模式,原型模式或者建造者模式(結(jié)構(gòu)更加復(fù)雜,使用靈活);原型模式并不一定需要繼承,但是它確實(shí)需要一個初始化的操作,工廠方

28、法模式 一定需要繼承,但是不一定需要初始化操作;使用裝飾者模式或者組合模式的情況通常也口j以使用原型模式來獲得益處; 單例模式中,只要將構(gòu)造方法的訪問權(quán)限設(shè)置為private型,就可以實(shí)現(xiàn)單例。 但是原型模式的clone方法直接無視構(gòu)造方法的權(quán)限來生成新的對象,所以, 單例模式與原型模式是沖突的,在使用時要特別注意。源碼下載https:/gi thub. com/zhaozepeng/design-patterns/tree/master/singletonpa ttcr nhttp:/www. tekbroaden. com/singletonjava. html?hmsr=toutia o. i

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論