本文定位于理解和總結(jié)《Effective Java》的所講內(nèi)容,而不是翻譯逐工,因此不當(dāng)之處爹袁,還請(qǐng)廣大網(wǎng)友指出。
關(guān)于單例
單例是指僅需要被實(shí)例化一次的類(lèi)砸抛,在編程實(shí)踐中往往用于實(shí)現(xiàn)那些僅需要一個(gè)對(duì)象的系統(tǒng)模塊。單例模式的實(shí)現(xiàn)有很多種树枫,具體可參見(jiàn)這篇博客直焙,單例模式對(duì)其具體實(shí)現(xiàn)的基本要求只有兩個(gè):線程安全和有且僅有一個(gè)實(shí)例。
本文并不討論單例模式本身的優(yōu)劣砂轻,盡管這類(lèi)討論一直都有并將持續(xù)下去奔誓,實(shí)際上,單例模式廣泛應(yīng)用于編程實(shí)踐舔清,本文的主題是如何優(yōu)雅地實(shí)現(xiàn)單例丝里。
線程安全
線程安全分為兩部分,創(chuàng)建和獲取實(shí)例時(shí)的線程安全以及調(diào)用單例其他方法時(shí)的線程安全体谒,后一部分的線程安全由客戶端程序員保證,在此不做討論臼婆,實(shí)際上抒痒,創(chuàng)建和獲取實(shí)例時(shí)的線程安全,目前大多數(shù)的實(shí)現(xiàn)是可以保證的颁褂,比如:
// Singleton with public final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
// other methods
public void leaveTheBuilding() { ... }
}
類(lèi)加載過(guò)程的線程安全性保證了上述實(shí)現(xiàn)中實(shí)例創(chuàng)建過(guò)程的線程安全故响,而獲取該實(shí)例過(guò)程的線程安全性是顯而易見(jiàn)的(由public關(guān)鍵字決定)。
實(shí)際上颁独,對(duì)于大多數(shù)的單例實(shí)現(xiàn)形式而言彩届,線程安全是容易保證的,而有且僅有一個(gè)實(shí)例這個(gè)要求卻難以保證誓酒,往往需要增加一些特殊處理樟蠕。比如上述實(shí)現(xiàn)中贮聂,仍然可以通過(guò)反射的方法來(lái)創(chuàng)建一個(gè)新的實(shí)例;或者上述類(lèi)在實(shí)現(xiàn)Serializable接口之后寨辩,在反序列化該類(lèi)時(shí)吓懈,可以獲取一個(gè)新的實(shí)例。
關(guān)于如何增加“特殊處理”來(lái)保證上述實(shí)現(xiàn)中有且只有一個(gè)實(shí)例靡狞,將會(huì)在后續(xù)的文章中給出耻警。
有且僅有一個(gè)實(shí)例
很多人認(rèn)為線程安全比有且僅有一個(gè)實(shí)例更難保證,其實(shí)不然甸怕。要保證單例模式有且僅有一個(gè)實(shí)例甘穿,除了將構(gòu)造器設(shè)置為private之外,還要求實(shí)現(xiàn)類(lèi)能夠抵抗反射攻擊以及反序列化攻擊(在反序列化過(guò)程中產(chǎn)生新的實(shí)例)梢杭。在Java 1.5之后增加的枚舉類(lèi)型天然的符合上述要求温兼,因此利用枚舉來(lái)實(shí)現(xiàn)單例模式是一個(gè)很好的方案,遺憾的是式曲,目前該方法并沒(méi)有被廣泛采用妨托。其實(shí)現(xiàn)非常簡(jiǎn)單,如下:
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding(){ ... }
}
- 枚舉構(gòu)造器的訪問(wèn)修飾符只能是private
這一特性保證了無(wú)法直接使用new關(guān)鍵字創(chuàng)建枚舉對(duì)象吝羞,既除了枚舉中定義的對(duì)象之外兰伤,外部程序無(wú)法創(chuàng)建對(duì)象。 - 不能通過(guò)反射調(diào)用構(gòu)造器創(chuàng)建新的枚舉實(shí)例
通過(guò)反射調(diào)用枚舉構(gòu)造器時(shí)會(huì)拋出java.lang.IllegalArgumentException異常钧排。 - 枚舉類(lèi)型的反序列化過(guò)程不會(huì)產(chǎn)生新的實(shí)例敦腔。
這一點(diǎn)由枚舉類(lèi)型本身保證,客戶端程序員無(wú)需做任何特殊處理恨溜。
當(dāng)然符衔,使用枚舉來(lái)實(shí)現(xiàn)單例模式也是線程安全的,也是通過(guò)類(lèi)加載過(guò)程的線程安全性來(lái)保證的糟袁。
綜上判族,在所有單例模式的實(shí)現(xiàn)中,枚舉以最優(yōu)雅的方式實(shí)現(xiàn)了線程安全项戴,保證了有且僅有一個(gè)實(shí)例形帮,并且使用起來(lái)非常簡(jiǎn)單,因此下次需要一個(gè)單例模式時(shí)周叮,請(qǐng)考慮使用枚舉辩撑。