單例模式解釋
單例模式是一種對象創(chuàng)建性模式恭朗,使用單例模式,可以保證為一個類只生成唯一的實例對象哥攘。也就是說剖煌,在整個程序空間中,該類只存在一個實例對象逝淹。
單例模式的要點(diǎn)有三個:
- 某個類只能有一個實例耕姊;
- 必須自行創(chuàng)建整個實例;
- 它必須自行向整個系統(tǒng)提供整個實例栅葡。
一茉兰、單例模式是什么?
單例模式最初的定義出現(xiàn)于《設(shè)計模式》:“保證一個類僅有一個實例欣簇,并提供一個訪問它的全局訪問點(diǎn)规脸。”
Java中單例模式定義醉蚁;“一個類有且僅有一個實例燃辖,并且自行實例化向整個系統(tǒng)提供該實例⊥鳎”
二黔龟、為什么用單例模式?
對于系統(tǒng)中的某些類來說滥玷,只有一個實例很重要氏身。例如,一個系統(tǒng)中可以存在多個打印任務(wù)惑畴,但是只能有一個正在工作的任務(wù)蛋欣;一個系統(tǒng)只有有一個窗口管理器或文件系統(tǒng);一個系統(tǒng)只能有一個計時工具或ID生成器如贷。如在Windows OS 中就只能打開一個任務(wù)管理器陷虎。如果不使用機(jī)制對窗口對象進(jìn)行唯一化到踏,將彈出多個窗口,如果這些窗口顯示的內(nèi)容完全一致尚猿,則重復(fù)對象窝稿,浪費(fèi)內(nèi)存資源;如果這些窗口顯示的內(nèi)容不一致凿掂,則意味著某一瞬間系統(tǒng)有多個狀態(tài)伴榔,與實際不符,也會為用戶帶來誤解庄萎,不知道哪一個才是真實的狀態(tài)踪少。因此有時確保系統(tǒng)中某個對象的唯一性即一個類只能有一個實例是非常重要的。
如何保證一個類只有一個實例并且這個實例易于被訪問呢糠涛?定義一個全局變量可以確保對象隨時都可以被訪問援奢,但不能防止我們實例化多個對象。一個更好的解決辦法是讓類自身負(fù)責(zé)保存它的唯一實例脱羡。這個類可以保證沒有其他實例被創(chuàng)建萝究,并且它可以提供一個訪問該實例的方法免都。這就是單例模式的模式動機(jī)锉罐。
三、單例模式特點(diǎn)
單例模式特點(diǎn)有三個
- 1绕娘、單例類只能有一個實例脓规。
- 2、單例類必須自己創(chuàng)建自己的唯一實例险领。
- 3侨舆、單例類必須給其他對象(整個系統(tǒng))提供這一實例。
從具體實現(xiàn)角度分析绢陌,一是單例模式的類只提供私有的(private)構(gòu)造函數(shù)挨下,二是類定義中含有一個該類的靜態(tài)私有(private static)對象,三是該類提供了一個靜態(tài)的(static)公有的(public)函數(shù)用于創(chuàng)建或獲取它本身的靜態(tài)私有對象脐湾。
四臭笆、Java中幾種常見單例模式寫法
通過上面的介紹你是不是對單例模式有了一個總的概念?沒有秤掌,那接下來繼續(xù)給你們放大招愁铺。
基于單例模式特點(diǎn),單例對象通常作為程序中存放配置信息的載體(想想Android中的Application經(jīng)常在里面做一些配置的初始化)闻鉴,因為它能夠保證其他對象讀取到一致的信息茵乱。例如在某個服務(wù)器程序中,該服務(wù)器的配置信息可能存放在數(shù)據(jù)庫或 文件中孟岛,這些配置數(shù)據(jù)由某個單例對象統(tǒng)一讀取瓶竭,服務(wù)進(jìn)程中的其他對象如果要獲取這些配置信息督勺,只需訪問該單例對象即可。這種方式極大地簡化了在復(fù)雜環(huán)境 下斤贰,尤其是多線程環(huán)境下的配置管理玷氏,但是隨著應(yīng)用場景的不同,也可能帶來一些同步問題腋舌。
五盏触、單例模式深入分析
單例模式適合一個類只有一個實例的情況, 比如窗口管理器块饺,打印緩沖池和文件系統(tǒng)赞辩,它們都是原型的例子。典型的情況是授艰,那些對象的類型被遍及一個軟件系統(tǒng)的不同對象訪問辨嗽,因此需要一個全局的訪問指針,這便是總所周知的單例模式的應(yīng)用淮腾。當(dāng)然這只有在你確信你不再需要任何多于一個的實例的情況下糟需。
六、 使用場景及代碼實現(xiàn)
下面就舉例來說明下:
單例模式的第一個版本谷朝,“餓漢式”洲押,也就是當(dāng)類加載進(jìn)來的就立即實例化對象,但是這種方式比較的消耗計算機(jī)資源圆凰。如下:
public class Foo {
// 在類被加載進(jìn)入內(nèi)存的時候就創(chuàng)建單一的Foo對象
public static final Foo foo = new Foo();
// 構(gòu)造函數(shù)私有化
private Foo() {
}
// 提供一個全局的靜態(tài)方法
public static Foo getFoo() {
return foo;
}
}
單例模式的第二個版本杈帐,“懶漢式”,在單線程下能夠非常好的工作专钉,但是在多線程下存在線程安全問題挑童,如下:
// 這種方式在需要使用的時候才實例化
public class Foo {
private static Foo foo;
// 構(gòu)造函數(shù)私有化
private Foo() {
}
// 提供一個全局的靜態(tài)方法
public static Foo getFoo() {
if (foo == null) {
foo = new Foo();
}
return foo;
}
}
單例模式的第三個版本,為解決多線程問題跃须,采用了對函數(shù)進(jìn)行同步的方式站叼,但是比較浪費(fèi)資源,因為每次都要進(jìn)行同步檢查菇民,而實際中真正需要檢查只是第一次實例化的時候尽楔,如下:
public class Foo {
private static Foo foo;
// 構(gòu)造函數(shù)私有化
private Foo() {
}
// 提供一個全局的靜態(tài)方法,使用同步方法
public static synchronized Foo getFoo() {
if (foo == null) {
foo = new Foo();
}
return foo;
}
}
單例模式的第四個版本玉雾,既解決了”懶漢式“的多線程問題翔试,又解決了資源浪費(fèi)的現(xiàn)象,看上去是一種不錯的選擇复旬,如下:
public class Foo {
private static Foo foo;
// 構(gòu)造函數(shù)私有化
private Foo() {
}
// 提供一個全局的靜態(tài)方法
public static Foo getFoo() {
if (foo == null) {
synchronized (Foo.class) {
if (foo == null) {
foo = new Foo();
}
}
}
return foo;
}
}
單例模式的優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):客戶端使用單例模式的實例的時候垦缅,只需要調(diào)用一個單一的方法即可生成一個唯一的實例,有利于節(jié)約資源驹碍。
缺點(diǎn):首先單例模式很難實現(xiàn)序列化壁涎,這就導(dǎo)致采用單例模式的類很難被持久化凡恍,當(dāng)然也很難通過網(wǎng)絡(luò)傳輸;其次由于單例采用靜態(tài)方法怔球,無法在繼承結(jié)構(gòu)中使用嚼酝。