單例設(shè)計模式所解決的問題就是:保證類的對象在內(nèi)存中唯一碗誉。
舉例:
A、B類都想要操作配置文件信息Config.java,所以在方法中都使用了Config con=new Config();但是這是兩個不同的對象。對兩者的操作互不影響米诉,不符合條件。
解決思路:?
1.不允許其他程序使用new創(chuàng)建該類對象篷帅。(別人new不可控)?
2.在該類中創(chuàng)建一個本類實例史侣。?
3.對外提供一個方法讓其他程序可以獲取該對象。
解決方法:單例模式魏身。
步驟:?
1.私有化該類的構(gòu)造函數(shù)?
2.通過new在本類中創(chuàng)建一個本類對象惊橱。?
3.定義一個共有的方法將創(chuàng)建的對象返回。
一箭昵、餓漢式
public class Singleton {
private Singleton(){
}
private static final Singleton instance=new Singleton();
public static Singleton getInstance(){
? ? return instance;
}
}?
為什么方法是靜態(tài)的:不能new對象卻想調(diào)用類中方法税朴,方法必然是靜態(tài)的,靜態(tài)方法只能調(diào)用靜態(tài)成員家制,所以對象也是靜態(tài)的正林。?
為什么對象的訪問修飾符是private,不能是public 嗎颤殴?不能觅廓,如果訪問修飾符是Public,則Single.s也可以得到該類對象涵但,這樣就造成了不可控杈绸。
二、懶漢式
1.靜態(tài)內(nèi)部類
public class Singleton2 {
//私有化構(gòu)造方法贤笆,外部無法直接new
private Singleton2(){
}
/**
* 由于內(nèi)部類不會在類的外部被使用蝇棉,所以只有在調(diào)用getInstance()方法時才會被加載。
* 同時依賴JVM的ClassLoader類加載機制保證了不會出現(xiàn)同步問題芥永。
*
*/
private static class Lazy{
? ? private static Singleton2 instance=new Singleton2();
}
private static Singleton2 getInstace(){
? ? return Lazy.instance;
}
}?
2.雙重檢測鎖
public class Singleton3 {
private Singleton3 (){}
private static Singleton3 instance=null;
public static Singleton3 getInstance(){
? ? if (instance==null) {
? ? ? ? synchronized(Singleton3.class){
? ? ? ? ? ? if (instance==null) {
? ? ? ? ? ? ? ? instance=new Singleton3();
? ? ? ? ? ? ? ? return instance;
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? return instance;
}
}
假設(shè)我們現(xiàn)在并沒有創(chuàng)建單例對象篡殷,即instance==null,那么我們調(diào)用getInstance方法的時候埋涧,會進(jìn)入if塊板辽,然后進(jìn)入同步代碼塊,此時棘催,別的線程如果想要創(chuàng)建Singleton3實例劲弦,就必須獲取鎖;等當(dāng)前線程創(chuàng)建完實例對象醇坝,釋放鎖之后邑跪,假設(shè)正巧有幾個線程已經(jīng)進(jìn)入了if塊中,它們會拿到鎖,進(jìn)入同步代碼塊画畅,但是由于進(jìn)行了判空操作砸琅,所以不會創(chuàng)建Singleton3實例,而是直接返回已經(jīng)創(chuàng)建好的Singleton3實例轴踱。如果有多個其他線程進(jìn)入了if塊症脂,當(dāng)它們依次進(jìn)入同步代碼塊的時候,同理也不會創(chuàng)建新的Singleton3實例淫僻。而沒有進(jìn)入if塊的線程诱篷,判空操作之后不滿足條件,進(jìn)不了if塊雳灵,而直接執(zhí)行了下一條語句return instance棕所;其后的線程調(diào)用getInstance方法時,只會判斷一次instance==null细办,不滿足條件直接返回Singleton3單例instance橙凳,這樣就大大提高了了執(zhí)行效率。
單例模式性能總結(jié)
優(yōu)缺點?
餓漢式 線程安全, 調(diào)用效率高 不能延遲加載?
懶漢式 線程安全, 可以延遲加載 調(diào)用效率不高?
雙重檢測鎖式 線程安全, 調(diào)用效率高, 可以延遲加載?
靜態(tài)內(nèi)部類式 線程安全, 調(diào)用效率高, 可以延遲加載
補充知識
類加載機制?
static關(guān)鍵字的作用是把類的成員變成類相關(guān),而不是實例相關(guān)笑撞,static塊會在類首次被用到的時候進(jìn)行加載岛啸,不是對象創(chuàng)建時,所以static塊具有線程安全性
普通初始化塊
當(dāng)Java創(chuàng)建一個對象時, 系統(tǒng)先為對象的所有實例變量分配內(nèi)存(前提是該類已經(jīng)被加載過了), 然后開始對這些實例變量進(jìn)行初始化, 順序是: 先執(zhí)行初始化塊或聲明實例變量時指定的初始值(這兩處執(zhí)行的順序與他們在源代碼中排列順序相同), 再執(zhí)行構(gòu)造器里指定的初始值.
靜態(tài)初始化塊?
又名類初始化塊(普通初始化塊負(fù)責(zé)對象初始化, 類初始化塊負(fù)責(zé)對類進(jìn)行初始化). 靜態(tài)初始化塊是類相關(guān)的, 系統(tǒng)將在類初始化階段靜態(tài)初始化, 而不是在創(chuàng)建對象時才執(zhí)行. 因此靜態(tài)初始化塊總是先于普通初始化塊執(zhí)行.
執(zhí)行順序?
系統(tǒng)在類初始化以及對象初始化時, 不僅會執(zhí)行本類的初始化塊[static/non-static], 而且還會一直上溯到j(luò)ava.lang.Object類, 先執(zhí)行Object類中的初始化塊[static/non-static], 然后執(zhí)行其父類的, 最后是自己.?
頂層類(初始化塊, 構(gòu)造器) -> … -> 父類(初始化塊, 構(gòu)造器) -> 本類(初始化塊, 構(gòu)造器)
小結(jié)
static{} 靜態(tài)初始化塊會在類加載過程中執(zhí)行;?
{} 則只是在對象初始化過程中執(zhí)行, 但先于構(gòu)造器;
內(nèi)部類
內(nèi)部類訪問權(quán)限
Java 外部類只有兩種訪問權(quán)限:public/default, 而內(nèi)部類則有四種訪問權(quán)限:private/default/protected/public. 而且內(nèi)部類還可以使用static修飾;內(nèi)部類可以擁有private訪問權(quán)限茴肥、protected訪問權(quán)限坚踩、public訪問權(quán)限及包訪問權(quán)限。如果成員內(nèi)部類Inner用private修飾瓤狐,則只能在外部類的內(nèi)部訪問瞬铸,如果用public修飾,則任何地方都能訪問础锐;如果用protected修飾嗓节,則只能在同一個包下或者繼承外部類的情況下訪問;如果是默認(rèn)訪問權(quán)限皆警,則只能在同一個包下訪問拦宣。這一點和外部類有一點不一樣,外部類只能被public和包訪問兩種權(quán)限修飾信姓。成員內(nèi)部類可以看做是外部類的一個成員鸵隧,所以可以像類的成員一樣擁有多種權(quán)限修飾。
內(nèi)部類分為成員內(nèi)部類與局部內(nèi)部類, 相對來說成員內(nèi)部類用途更廣泛, 局部內(nèi)部類用的較少(匿名內(nèi)部類除外), 成員內(nèi)部類又分為靜態(tài)(static)內(nèi)部類與非靜態(tài)內(nèi)部類, 這兩種成員內(nèi)部類同樣要遵守static與非static的約束(如static內(nèi)部類不能訪問外部類的非靜態(tài)成員等)
非靜態(tài)內(nèi)部類
非靜態(tài)內(nèi)部類在外部類內(nèi)使用時, 與平時使用的普通類沒有太大區(qū)別;
Java不允許在非static內(nèi)部類中定義static成員意推,除非是static final的常量類型
如果外部類成員變量, 內(nèi)部類成員變量與內(nèi)部類中的方法里面的局部變量有重名, 則可通過this, 外部類名.this加以區(qū)分.
非靜態(tài)內(nèi)部類的成員可以訪問外部類的private成員, 但反之不成立, 內(nèi)部類的成員不被外部類所感知. 如果外部類需要訪問內(nèi)部類中的private成員, 必須顯示創(chuàng)建內(nèi)部類實例, 而且內(nèi)部類的private權(quán)限對外部類也是不起作用的:
靜態(tài)內(nèi)部類
使用static修飾內(nèi)部類, 則該內(nèi)部類隸屬于該外部類本身, 而不屬于外部類的某個對象.
由于static的作用, 靜態(tài)內(nèi)部類不能訪問外部類的實例成員, 而反之不然;
匿名內(nèi)部類
如果(方法)局部變量需要被匿名內(nèi)部類訪問, 那么該局部變量需要使用final修飾.