今天開(kāi)始學(xué)習(xí)設(shè)計(jì)模式,借此機(jī)會(huì)學(xué)習(xí)并整理學(xué)習(xí)筆記。
設(shè)計(jì)模式是一門不區(qū)分語(yǔ)言的課程,什么樣的編程語(yǔ)言都可以用到設(shè)計(jì)模式。如果說(shuō)java語(yǔ)法規(guī)則比作武功招式的話,那么設(shè)計(jì)模式就是心法。
設(shè)計(jì)模式共有23種,常見(jiàn)的19種,最常用的9-10種。
設(shè)計(jì)模式分三種類型:創(chuàng)建型、結(jié)構(gòu)型、行為型;
其中創(chuàng)建型包含單例設(shè)計(jì)模式、工廠模式、抽象工廠模式、原型模式、建造者模式;結(jié)構(gòu)型包含代理模式、裝飾器模式、適配器模式、外觀模式、組合模式、享元模式、橋梁模式;行為型包含:策略模式、責(zé)任鏈模式、命令模式、中介者模式、模板方法模式、迭代器模式、訪問(wèn)者模式、觀察者模式、解釋器模式、備忘錄模式、狀態(tài)模式。
好了,湊字完畢,開(kāi)始今天的學(xué)習(xí)整理
Singleton設(shè)計(jì)模式,就是單例設(shè)計(jì)模式。
單例設(shè)計(jì)模式有句順口溜:單例一實(shí)例,私有構(gòu)造器
單例設(shè)計(jì)模式分為餓漢式和懶漢式。
餓漢式是用戶沒(méi)有請(qǐng)求你的要求時(shí),已經(jīng)把這個(gè)實(shí)例提前創(chuàng)建出來(lái)了;
懶漢式則是需只有外部需要調(diào)用getInstance的時(shí)候,才回去初始化這個(gè)單例。
這樣就有一個(gè)區(qū)別:
餓漢式:用空間換時(shí)間
懶漢式:用時(shí)間換空間
下面是具體代碼實(shí)現(xiàn):
singleton 單例設(shè)計(jì)模式 1:餓漢式 餓漢式優(yōu)缺點(diǎn): 優(yōu)點(diǎn): 實(shí)現(xiàn)簡(jiǎn)單,沒(méi)有多線程同步問(wèn)題(默認(rèn)條件下是線程安全的) 缺點(diǎn):當(dāng)類加載SingletonTest的時(shí)候,會(huì)初始化static的instance,靜態(tài)變量被創(chuàng)建并分配內(nèi)存空間,從此,這個(gè)static的instance對(duì)象便一直占著此段內(nèi)存(即使你并沒(méi)有使用該實(shí)例,當(dāng)類被卸載時(shí),靜態(tài)變量被摧毀,并釋放所占有的內(nèi)存,因此在某些特定條件下會(huì)耗費(fèi)內(nèi)存。 簡(jiǎn)單來(lái)說(shuō) 用空間換時(shí)間
為什么在餓漢式中不討論線程安全問(wèn)題: 因?yàn)轭惱锩娴撵o態(tài)實(shí)例都是由類加載器來(lái)負(fù)責(zé)初始化的,類加載器classLoader在出現(xiàn)類名的時(shí)候就會(huì)開(kāi)始工作,把類的靜態(tài)實(shí)例全部初始化,而且只初始化一次,所以一定是安全的,
public class Singleton { //靜態(tài)一實(shí)例 將自身實(shí)例化對(duì)象設(shè)置為一個(gè)屬性,并用static final修飾 private static Singleton instance = new Singleton();
//私有構(gòu)造器 private Singleton() { } //外部通過(guò)靜態(tài)方法返回該實(shí)例 public static Singleton getInstance() { return instance; } }
2:懶漢式 懶漢式優(yōu)缺點(diǎn): 優(yōu)點(diǎn):實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單,當(dāng)類SingletonTest被加載的時(shí)候,靜態(tài)變量static的instance未被創(chuàng)建并分配內(nèi)存空間,當(dāng)getInstance()方法第一次調(diào)用的時(shí),初始化instance變量,并分配內(nèi)存空間。在某些條件下這種方式會(huì)節(jié)省內(nèi)存。
缺點(diǎn):在多線程環(huán)境中這種方法是完全錯(cuò)誤的,根本不能保證單例的狀態(tài) 需要添加synchronized(鎖)
public class Singleton2 { //對(duì)象賦予null值 或者 不賦值 private static Singleton2 instance2;
//私有構(gòu)造器 prviate Singleton2() { } //外部獲取實(shí)例對(duì)象 此時(shí)為了線程安全 需要使用synchronized(鎖) public static synchronized Singleton2 getInstance2() { if(instance2 == null) { instance2 = new Singleton2(); } return instance2; } }
3:如何兼顧餓漢式與懶漢式的優(yōu)點(diǎn): 第一種:使用靜態(tài)內(nèi)部類的方法
優(yōu)點(diǎn):外部類加載時(shí)不予要立即加載內(nèi)部類,內(nèi)部類不被加載就不會(huì)初始化instance 故而占用內(nèi)存。當(dāng)Singleton 第一次加載時(shí),并不需要去加載Singleton3handler只有當(dāng)getInstance()方法第一次被 調(diào)用時(shí),才會(huì)去初始化Instance,第一次調(diào)用getInstance()方法會(huì)導(dǎo)致虛擬機(jī)加載在SingletonHangdler類,這種方法不僅能確保線程安全,也能保證單例的唯一性,同時(shí)也延遲單例的實(shí)例化,
public class Singleton3 { //構(gòu)建靜態(tài)內(nèi)部類 private static class Singleton3Handler{ private static Singleton3 instance3 = new Singleton3(); } //私有構(gòu)造器 private Singleton3() { } //外部獲取對(duì)象 public static Singleton3 getInstance() { return Singleton3.Singleton3Handler.instance3; } }
第二種:使用枚舉方式 優(yōu)點(diǎn): 線程是安全的
public enum Singleton4{ INSTANCE; public void method() { //TODO } } 在任何情況下,它都是一個(gè)單例 我們可以直接 Singleton4.INSTANCE 引用
第三種:使用DCL模式 需要使用Volatile 關(guān)鍵字 雙重鎖懶漢式( Double Check Lock 簡(jiǎn)稱 DCL ) 優(yōu)點(diǎn):只有對(duì)象需要被使用的時(shí)候才創(chuàng)建,第一次判斷instance21 == null 為了避免非必要枷鎖,當(dāng)?shù)谝淮渭虞d時(shí)才對(duì)實(shí)例進(jìn)行枷鎖在實(shí)例化。這樣就可以節(jié)約內(nèi)存空間,有可以保證線程安全。
缺點(diǎn):但是,由于jvm存在亂序執(zhí)行功能,DCL也會(huì)出現(xiàn)線程不安全的情況。
比如: instance21 = new Singleton2; 這個(gè)步驟,其實(shí)在jvm里面的執(zhí)行分為三步: 1:在堆內(nèi)開(kāi)辟內(nèi)存空間 2;在堆內(nèi)存中實(shí)例化Singleton2里面的各個(gè)參數(shù) 3: 把對(duì)象指向堆內(nèi)存 但是這個(gè)缺點(diǎn)在jdk1.6以后 只需要定義 為: private volatile static Singleton5 instance5 = null; 就可以 解決此問(wèn)題 。 volatile確保instance每次在均在主內(nèi)存中讀取,這樣雖然會(huì)犧牲一點(diǎn)效率。
public class Singleton5{ // 對(duì)象一實(shí)例 private volatile static Singleton5 instance5; //私有構(gòu)造器 private Singleton5() { } //DCL懶漢式 public static Singleton5 getInstance() { if(instance5==null) { synchronized(Singleton5.class){ if(instance5 == null) { instance5 = new Singleton5(); } } } return instance5; } }
需要注意的是:
需要延遲加載的情況下使用第一、第二種;
需要防止序列化問(wèn)題、反射攻擊使用第三種。
才疏學(xué)淺,如有錯(cuò)誤,懇請(qǐng)指教!
|