淺談使用單元素的枚舉類型實(shí)現(xiàn)單例模式

通常情況下怀各,我們寫單例模式的時(shí)候無非就是三個(gè)步驟:構(gòu)造器私有化听系,聲明私有靜態(tài)變量舍悯,提供靜態(tài)獲取實(shí)例的方法航棱。簡(jiǎn)單說就是以下這種方式:

class SingletonA {
    private static SingletonA instence = new SingletonA();
    private SingletonA() {
    }
    public static SingletonA getInstance() {
        return instence;
    }
}

這是最基本的單例模式的寫法睡雇,考慮到線程安全的問題,會(huì)用synchronized 關(guān)鍵字修飾getInstance()方法饮醇,另外還有餓漢式它抱、懶漢式、靜態(tài)內(nèi)部類驳阎、雙重校驗(yàn)鎖的寫法抗愁。
但是這種寫法存在缺陷,可以利用反射的方式來實(shí)例化多個(gè)不同的實(shí)例呵晚,如下所示:

private static void testReflex() {

        try {

            SingletonA s1 = SingletonA.getInstance();
            Class<SingletonA> cls = SingletonA.class;
            Constructor<SingletonA> constructor = cls
                    .getDeclaredConstructor(new Class[] {});
            constructor.setAccessible(true);
            SingletonA s2 = constructor.newInstance(new Object[] {});

            System.out.println(s1 == s2);//false
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

這種情況下蜘腌,就會(huì)出現(xiàn)多個(gè)不同的實(shí)例,從而導(dǎo)致一些亂七八糟的結(jié)果饵隙。
還有一種情況就是在序列化和反序列換的時(shí)候也會(huì)出現(xiàn)多個(gè)不同的實(shí)例撮珠,如下:

class SingletonB implements Serializable {

    private static SingletonB instence = new SingletonB();

    private SingletonB() {
    }

    public static SingletonB getInstance() {
        return instence;
    }
}
private static void testSingletonB() {
        File file = new File("singleton");
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream(file));
            SingletonB SingletonB1 = SingletonB.getInstance();

            oos.writeObject(SingletonB1);
            oos.close();
            ois = new ObjectInputStream(new FileInputStream(file));
            SingletonB SingletonB2 = (SingletonB) ois.readObject();
            System.out.println(SingletonB1 == SingletonB2);//false

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

這種情況也會(huì)出現(xiàn)有多個(gè)實(shí)例的問題,這個(gè)問題可以在類中添加readResolve()方法來避免金矛,即:

class SingletonB implements Serializable {

    private static SingletonB instence = new SingletonB();

    private SingletonB() {
    }

    public static SingletonB getInstance() {
        return instence;
    }

    // 不添加該方法則會(huì)出現(xiàn) 反序列化時(shí)出現(xiàn)多個(gè)實(shí)例的問題
    public Object readResolve() {
        return instence;
    }
}

這樣在反序列化的時(shí)候就不會(huì)出現(xiàn)多個(gè)實(shí)例芯急。

使用單元素的枚舉實(shí)現(xiàn)單例模式

一個(gè)最簡(jiǎn)單的POJO類,如下:

enum SingletonC implements Serializable {
    INSTANCE;
    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

}

測(cè)試方法:

   private static void testEnum() {
        File file = new File("singletonEnum");
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {

            oos = new ObjectOutputStream(new FileOutputStream(file));
            SingletonC singleton = SingletonC.INSTANCE;
            oos.writeObject(SingletonC.INSTANCE);
            oos.close();
            ois = new ObjectInputStream(new FileInputStream(file));
            SingletonC singleton2 = (SingletonC) ois.readObject();
            System.out.println(singleton == singleton2);//true

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

這種實(shí)現(xiàn)單例模式的方式是在 1.5之后才出現(xiàn)的

這種方法在功能上與公有域方法相近驶俊,但是它更加簡(jiǎn)潔娶耍,無償提供了序列化機(jī)制,絕對(duì)防止多次實(shí)例化饼酿,即使是在面對(duì)復(fù)雜序列化或者反射攻擊的時(shí)候榕酒。雖然這種方法還沒有廣泛采用,但是單元素的枚舉類型已經(jīng)成為實(shí)現(xiàn)Singleton的最佳方法故俐。 —-《Effective Java 中文版 第二版》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末想鹰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子药版,更是在濱河造成了極大的恐慌辑舷,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件槽片,死亡現(xiàn)場(chǎng)離奇詭異何缓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)筐乳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門歌殃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蝙云,你說我怎么就攤上這事氓皱。” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵波材,是天一觀的道長(zhǎng)股淡。 經(jīng)常有香客問我,道長(zhǎng)廷区,這世上最難降的妖魔是什么唯灵? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮隙轻,結(jié)果婚禮上埠帕,老公的妹妹穿的比我還像新娘。我一直安慰自己玖绿,他們只是感情好敛瓷,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斑匪,像睡著了一般呐籽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚀瘸,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天狡蝶,我揣著相機(jī)與錄音,去河邊找鬼贮勃。 笑死贪惹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的寂嘉。 我是一名探鬼主播馍乙,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼垫释!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起撑瞧,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤棵譬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后预伺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體订咸,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年酬诀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脏嚷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瞒御,死狀恐怖父叙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤趾唱,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布涌乳,位于F島的核電站,受9級(jí)特大地震影響甜癞,放射性物質(zhì)發(fā)生泄漏夕晓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一悠咱、第九天 我趴在偏房一處隱蔽的房頂上張望蒸辆。 院中可真熱鬧,春花似錦析既、人聲如沸躬贡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逗宜。三九已至,卻和暖如春空骚,著一層夾襖步出監(jiān)牢的瞬間纺讲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工囤屹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留熬甚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓肋坚,卻偏偏與公主長(zhǎng)得像乡括,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子智厌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 單例模式(SingletonPattern)一般被認(rèn)為是最簡(jiǎn)單诲泌、最易理解的設(shè)計(jì)模式,也因?yàn)樗暮?jiǎn)潔易懂铣鹏,是項(xiàng)目中最...
    成熱了閱讀 4,227評(píng)論 4 34
  • 1.單例模式概述 (1)引言 單例模式是應(yīng)用最廣的模式之一敷扫,也是23種設(shè)計(jì)模式中最基本的一個(gè)。本文旨在總結(jié)通過Ja...
    曹豐斌閱讀 2,866評(píng)論 6 47
  • 前言 本文主要參考 那些年诚卸,我們一起寫過的“單例模式”葵第。 何為單例模式? 顧名思義合溺,單例模式就是保證一個(gè)類僅有一個(gè)...
    tandeneck閱讀 2,485評(píng)論 1 8
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理卒密,服務(wù)發(fā)現(xiàn),斷路器棠赛,智...
    卡卡羅2017閱讀 134,600評(píng)論 18 139
  • 1 場(chǎng)景問題# 1.1 讀取配置文件的內(nèi)容## 考慮這樣一個(gè)應(yīng)用哮奇,讀取配置文件的內(nèi)容膛腐。 很多應(yīng)用項(xiàng)目,都有與應(yīng)用相...
    七寸知架構(gòu)閱讀 6,652評(píng)論 12 68