什么是單例模式
單例模式是應(yīng)用最廣的設(shè)計(jì)模式之一。
在很多時(shí)候秸讹,一個(gè)應(yīng)用程序?qū)τ谝粋€(gè)類(lèi)只需要一個(gè)全局的實(shí)例對(duì)象以供調(diào)用雅倒,創(chuàng)建多個(gè)對(duì)象會(huì)導(dǎo)致對(duì)系統(tǒng)資源的浪費(fèi)且沒(méi)有任何意義,單例模式就為此而生蔑匣。
單例模式的實(shí)現(xiàn)思路
應(yīng)確保這個(gè)類(lèi)只能有一個(gè)實(shí)例,且自行實(shí)例化向整個(gè)系統(tǒng)提供這個(gè)實(shí)例凿将。
為了能夠?qū)崿F(xiàn)上述思路价脾,我們可有想出下面的幾個(gè)點(diǎn)。
- 為了確保調(diào)用者無(wú)法自行創(chuàng)建該類(lèi)的實(shí)例化對(duì)象彼棍,所以應(yīng)該將該類(lèi)的構(gòu)造方法用private修飾膳算。
- 因?yàn)樵搶?duì)象無(wú)法通過(guò)new創(chuàng)建涕蜂,所以類(lèi)中應(yīng)有一個(gè)該類(lèi)的靜態(tài)實(shí)例化對(duì)象作為調(diào)用的返回值映琳。
- 為了將上述的靜態(tài)實(shí)例化對(duì)象返回,應(yīng)有一個(gè)公開(kāi)的方法將該對(duì)象作為返回值返回萨西。
單例的各種實(shí)現(xiàn)方式
有了思路,我們來(lái)一起看一下實(shí)現(xiàn)方法葱跋。
懶漢式單例
所謂懶漢式源梭,顧名思義它比較“懶”,它在類(lèi)加載的時(shí)候并不會(huì)去實(shí)例化應(yīng)返回的單例對(duì)象废麻,而是當(dāng)調(diào)用時(shí)才去判空來(lái)判斷是否創(chuàng)建該對(duì)象然后才作為返回值返回。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
餓漢式單例
而餓漢式卻與上面的懶漢是不同油宜,它不管你是否調(diào)用怜姿,我在類(lèi)加載的時(shí)候就去創(chuàng)建這個(gè)對(duì)象,方法調(diào)用時(shí)直接返回粪薛。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
以上提到的兩種并沒(méi)有考慮多線程的情況搏恤,再高并發(fā)的情況下湃交,仍有可能會(huì)出現(xiàn)創(chuàng)建多個(gè)對(duì)象的可能,懶漢式雖然在方法前加上synchronized
可以解決搞莺,但由于每次調(diào)用都需要去進(jìn)行同步,造成不必要的開(kāi)銷(xiāo)迈喉,所以這種方式不推薦使用。
DCL單例
全稱(chēng)是Doucle Check Lock,這種實(shí)現(xiàn)方式既能在需要時(shí)才初始化單例孩革,又能夠保證線程安全得运,但是由于java內(nèi)存模型的原因或在高并發(fā)情況下仍有可能會(huì)失敗,但是由于Android開(kāi)發(fā)過(guò)程中一般不會(huì)出現(xiàn)高并發(fā)的情況熔掺,所以仍是使用最多的一種單例模式。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
解釋一下這里為什么會(huì)用到雙重判空推沸,簡(jiǎn)單來(lái)說(shuō)就是由于JVM的原因券坞,如果你僅僅是單重判空,可能會(huì)發(fā)生這樣的情況:假設(shè)有兩個(gè)線程深浮,在第一個(gè)線程判空成功準(zhǔn)備去創(chuàng)建該對(duì)象的這個(gè)時(shí)候眠冈,可能另一個(gè)線程剛好完成了創(chuàng)建,這個(gè)時(shí)候就出現(xiàn)了問(wèn)題蜗顽,所以在這里我們采用這種寫(xiě)法,能有效的避免這種情況的發(fā)生忿等。
靜態(tài)內(nèi)部類(lèi)單例
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
}
由于靜態(tài)內(nèi)部類(lèi)是線程安全的崔挖,所以這種方式肯定是沒(méi)有任何問(wèn)題的。
枚舉單例
public enum Singleton {
INSTANCE;
}
和上面的方式一樣薛匪,只不過(guò)用枚舉來(lái)實(shí)現(xiàn)脓鹃,因?yàn)槊杜e也默認(rèn)是線程安全的。只不過(guò)由于Android開(kāi)發(fā)中并不推薦使用枚舉類(lèi),所以很少看到岩齿。
單例模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 減少了內(nèi)存開(kāi)支(只能創(chuàng)建一個(gè)對(duì)象)苞俘。
- 減少了性能開(kāi)銷(xiāo)(通過(guò)永駐內(nèi)存來(lái)解決對(duì)象的頻繁創(chuàng)建與銷(xiāo)毀)。
- 避免對(duì)資源的重復(fù)占用(只有一個(gè)對(duì)象去對(duì)另一個(gè)對(duì)象進(jìn)行操作)襟诸。
- 優(yōu)化了資源訪問(wèn)(可以全局訪問(wèn))基协。
缺點(diǎn)
- 擴(kuò)展性差(單例模式一般沒(méi)有接口,所以擴(kuò)展性差澜驮,如果要擴(kuò)展,一般只能通過(guò)直接修改代碼的方式來(lái)擴(kuò)展)悍缠。
- 如果單例對(duì)象持有Context耐量,容易引發(fā)內(nèi)存泄露,所以一般傳遞給單例對(duì)象的Context都是Application Context廊蜒。