0. 前言
在Java對(duì)象的創(chuàng)建時(shí),單例模式使用尤其多,同時(shí)也是個(gè)面試必問的基礎(chǔ)題。很多時(shí)候面試官想問的無非是懶漢式的雙重檢驗(yàn)鎖。但是其實(shí)還有兩種更加直觀高效的寫法,也是《Effective Java》中所推薦的寫法。
1. 靜態(tài)內(nèi)部類(static nested class)
public class Singleton {
public static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
由于靜態(tài)內(nèi)部類SingletonHolder
只有在getInstance()
方法第一次被調(diào)用時(shí),才會(huì)被加載,而且構(gòu)造函數(shù)為private,因此該種方式實(shí)現(xiàn)了懶漢式的單例模式。不僅如此,根據(jù)JVM本身機(jī)制,靜態(tài)內(nèi)部類的加載已經(jīng)實(shí)現(xiàn)了線程安全。所以給大家推薦這種寫法。
2. 枚舉法(Enum)
在《Effective Java》最后推薦了這樣一個(gè)寫法,簡直有點(diǎn)顛覆,不僅超級(jí)簡單,而且保證了線程安全。這里引用一下,此方法無償提供了序列化機(jī)制,絕對(duì)防止多次實(shí)例化,及時(shí)面對(duì)復(fù)雜的序列化或者反射攻擊。單元素枚舉類型已經(jīng)成為實(shí)現(xiàn)Singleton的最佳方法。
public enum Singleton {
INSTANCE;
}
3. 枚舉法探究
很多人會(huì)對(duì)枚舉法實(shí)現(xiàn)的單例模式很不理解。這里需要深入理解的是兩個(gè)點(diǎn):
- 枚舉類實(shí)現(xiàn)其實(shí)省略了
private
類型的構(gòu)造函數(shù) - 枚舉類的域(field)其實(shí)是相應(yīng)的enum類型的一個(gè)實(shí)例對(duì)象
對(duì)于第一點(diǎn)實(shí)際上enum內(nèi)部是如下代碼:
public enum Singleton {
INSTANCE;
// 這里隱藏了一個(gè)空的私有構(gòu)造方法
private Singleton () {}
}
對(duì)于一個(gè)標(biāo)準(zhǔn)的enum單例模式,最優(yōu)秀的寫法還是實(shí)現(xiàn)接口的形式:
// 定義單例模式中需要完成的代碼邏輯
public interface MySingleton {
void doSomething();
}
public enum Singleton implements MySingleton {
INSTANCE {
@Override
public void doSomething() {
System.out.println("complete singleton");
}
};
public static MySingleton getInstance() {
return Singleton.INSTANCE;
}
}
到這里,相信各位同學(xué)一定非常明白了。以后寫讓人眼前一亮的effective code吧~
4. 一些資料
以下列舉了枚舉法探究的一些很好的鏈接,強(qiáng)烈建議讀者點(diǎn)進(jìn)去用心體會(huì)一下。