Java單例模式的實現(xiàn)與破壞

單例模式是一種設(shè)計模式,是在整個運行過程中只需要產(chǎn)生一個實例实柠。那么怎樣去創(chuàng)建呢,以下提供了幾種方案善涨。

一窒盐、創(chuàng)建單例對象

懶漢式

public class TestSingleton {

    // 構(gòu)造方法私有化
    private TestSingleton(){}

    // 聲明實例
    private static TestSingleton singleton;

    // 提供外部調(diào)用方法草则,生成并獲取實例
    public static TestSingleton getInstance() {
        if(singleton == null) {
            singleton = new TestSingleton();
        }
        return singleton;
    }
}

此方案是以時間換空間,啟動時并不會執(zhí)行任何操作蟹漓,只有被調(diào)用時炕横,采取實例化對象。不過這種方法在多線程下不安全葡粒,因為兩個線程如果同時調(diào)用時份殿,會同時通過非空驗證的驗證,造成創(chuàng)建兩個對象的后果嗽交,有悖設(shè)計初衷卿嘲。

針對多線程問題,應(yīng)該加入雙重非空判斷:

public class TestSingleton {

    // 構(gòu)造方法私有化
    private TestSingleton(){}

    // 聲明實例
    private static volatile TestSingleton singleton;

    // 提供外部調(diào)用方法夫壁,生成并獲取實例
    public static TestSingleton getInstance() {
        if(singleton == null) {
            synchronized (TestSingleton.class) {
                if(singleton == null) {
                    singleton = new TestSingleton();
                }
            }
        }
        return singleton;
    }
}

餓漢式

public class TestSingleton {

    // 構(gòu)造方法私有化
    private TestSingleton(){}

    // 聲明并生成實例
    private static TestSingleton singleton = new TestSingleton();

    // 提供外部調(diào)用方法拾枣,獲取實例
    public static TestSingleton getInstance() {
        return singleton;
    }
}

以空間換時間,類一加載時盒让,就對其進行實例化梅肤,后面調(diào)用時直接提供對象實例。

靜態(tài)內(nèi)部類實現(xiàn)懶加載

public class TestSingleton {

    // 構(gòu)造方法私有化
    private TestSingleton(){}

    private static class TestSingletonFactory{
        private static TestSingleton singleton = new TestSingleton();
    }

    // 提供外部調(diào)用方法邑茄,獲取實例
    public static TestSingleton getInstance() {
        return TestSingletonFactory.singleton;
    }
}

當(dāng)getInstance方法被調(diào)用時姨蝴,才會初始化靜態(tài)內(nèi)部類TestSingletonFactory的靜態(tài)變量singleton。此處由JVM來保障線程安全肺缕。

二似扔、破壞單例

實現(xiàn)單例后,按照預(yù)期結(jié)果應(yīng)該所有對象都是同一個對象搓谆。但是以下有幾種情況可以破壞單例的性質(zhì)炒辉。

首先讓單例類實現(xiàn)Serializable, Cloneable接口,以便實驗泉手。

public class TestSingleton implements Serializable, Cloneable{

    private static final long serialVersionUID = 1L;

    // 構(gòu)造方法私有化
    private TestSingleton(){}

    private static class TestSingletonFactory{
        private static TestSingleton singleton = new TestSingleton();
    }

    // 提供外部調(diào)用方法黔寇,獲取實例
    public static TestSingleton getInstance() {
        return TestSingletonFactory.singleton;
    }
}
  • 序列化
// 獲取實例
TestSingleton originSingleton = TestSingleton.getInstance();
// 寫出對象
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(originSingleton);
// 寫入對象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
TestSingleton serializeSingleton = (TestSingleton) ois.readObject();
// 判斷兩個對象是否相等
System.out.println(originSingleton == serializeSingleton); // false
  • 反射
// 反射
Class<TestSingleton> clazz = TestSingleton.class;
// 獲取無參構(gòu)造函數(shù)
Constructor<TestSingleton> constructor = clazz.getDeclaredConstructor();
// 將私有設(shè)置為可見
constructor.setAccessible(true);
// 用構(gòu)造器生成實例
TestSingleton instance = constructor.newInstance();
// 判斷兩個對象是否相等
System.out.println(originSingleton == instance); // false
  • 克隆
// 克隆
TestSingleton clone = (TestSingleton) originSingleton.clone();
System.out.println(originSingleton == clone); // false

三、修復(fù)破壞

對于這種預(yù)料之外的結(jié)果斩萌,我們應(yīng)該怎樣去控制呢缝裤?

  • 序列化

添加readResolve方法,返回Object颊郎。

  • 反射

添加全局可見變量憋飞,如果再次調(diào)用構(gòu)造方法生成實例時,拋出運行時錯誤姆吭。

  • 克隆

重寫clone方法榛做,直接返回單例對象。

public class TestSingleton implements Serializable, Cloneable{

    private static final long serialVersionUID = 1L;

    private static volatile boolean isCreated = false;//默認是第一次創(chuàng)建

    // 構(gòu)造方法私有化
    private TestSingleton(){
        if(isCreated) {
            throw new RuntimeException("實例已經(jīng)被創(chuàng)建");
        }
        isCreated = true;
    }

    private static class TestSingletonFactory{
        private static TestSingleton singleton = new TestSingleton();
    }

    // 提供外部調(diào)用方法,獲取實例
    public static TestSingleton getInstance() {
        return TestSingletonFactory.singleton;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return getInstance();
    }

    /**
     * 防止序列化破環(huán)
     * @return
     */
    private Object readResolve() {
            return getInstance();
        }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末检眯,一起剝皮案震驚了整個濱河市厘擂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锰瘸,老刑警劉巖刽严,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異避凝,居然都是意外死亡舞萄,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門管削,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鹏氧,“玉大人,你說我怎么就攤上這事佩谣。” “怎么了实蓬?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵茸俭,是天一觀的道長。 經(jīng)常有香客問我安皱,道長调鬓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任酌伊,我火速辦了婚禮腾窝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘居砖。我一直安慰自己虹脯,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布奏候。 她就那樣靜靜地躺著循集,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蔗草。 梳的紋絲不亂的頭發(fā)上咒彤,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音咒精,去河邊找鬼镶柱。 笑死,一個胖子當(dāng)著我的面吹牛模叙,可吹牛的內(nèi)容都是我干的歇拆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼查吊!你這毒婦竟也來了谐区?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤逻卖,失蹤者是張志新(化名)和其女友劉穎宋列,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體评也,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡炼杖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了盗迟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坤邪。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖罚缕,靈堂內(nèi)的尸體忽然破棺而出艇纺,到底是詐尸還是另有隱情,我是刑警寧澤邮弹,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布黔衡,位于F島的核電站,受9級特大地震影響腌乡,放射性物質(zhì)發(fā)生泄漏盟劫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一与纽、第九天 我趴在偏房一處隱蔽的房頂上張望侣签。 院中可真熱鬧,春花似錦急迂、人聲如沸影所。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽型檀。三九已至,卻和暖如春听盖,著一層夾襖步出監(jiān)牢的瞬間胀溺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工皆看, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留仓坞,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓腰吟,卻偏偏與公主長得像无埃,于是被迫代替她去往敵國和親徙瓶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

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