JAVA設(shè)計模式(一)單例模式

引言

單例模式(Singleton Pattern)是 Java 中最簡單的設(shè)計模式之一。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
這種模式涉及到一個單一的類,該類負(fù)責(zé)創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
設(shè)計原則:
1、單例類只能有一個實例,所以這個示例必須私有并且只能創(chuàng)建一次;
2、單例類必須自己創(chuàng)建自己的唯一實例,所以它的構(gòu)造方法必須私有;
3、單例類必須給所有其他對象提供這一實例,提供對外的返回示例的方法,這個方法只能是靜態(tài)方法,因為別人無法創(chuàng)建實例,從而這個實例對象也為靜態(tài)屬性。
UML圖如下:


單例模式.png

優(yōu)缺點分析

1.優(yōu)點:
1>內(nèi)存中僅此一份,減少了內(nèi)存的開銷,尤其是頻繁的創(chuàng)建和銷毀實例,如頁面緩存;
2>避免對資源的多重占用,如多線程對文件的寫操作;
2.缺點:沒有接口,不能繼承,擴(kuò)展性較差。
單例模式有多種寫法,這里我們學(xué)習(xí)其中的6中方法:

餓漢式(線程安全)

package com.qicode.kakaxicm.designpattern.singleton;

/**
 * Created by chenming on 2018/6/11
 * 餓漢式,線程安全
 */
public class HungrySingleton {
    private static HungrySingleton sInstance = new HungrySingleton();

    private HungrySingleton() {
    }

    public static HungrySingleton getsInstance() {
        return sInstance;
    }

    public void showMsg(String s) {
        System.out.println(this.getClass().getSimpleName() + "--" + this + "--" + s);
    }
}

這種方式在類加載時就完成了初始化,類加載較慢,單獲取對象速度快。它基于類加載機(jī)制,避免了線程安全問題。如果始終沒用到這個實例,則會造成內(nèi)存浪費。

懶漢式(線程不安全)

package com.qicode.kakaxicm.designpattern.singleton;

/**
 * Created by chenming on 2018/6/11
 */
public class LazySingleton {

    private static LazySingleton sInstance;
    private LazySingleton() {

    }

    public static LazySingleton getsInstance(){
        if(sInstance == null){
            sInstance = new LazySingleton();
        }
        return sInstance;
    }

    public void showMsg(String s) {
        System.out.println(this.getClass().getSimpleName() + "--" + this + "--" + s);
    }
}

這種方式在首次使用的時候才構(gòu)造實例,節(jié)約資源,多線程不安全。

懶漢式(線程安全)

package com.qicode.kakaxicm.designpattern.singleton;

/**
 * Created by chenming on 2018/6/11
 */
public class LazySingleton {

    private static LazySingleton sInstance;
    private LazySingleton() {

    }

    public static synchronized LazySingleton getsInstance(){
        if(sInstance == null){
            sInstance = new LazySingleton();
        }
        return sInstance;
    }

    public void showMsg(String s) {
        System.out.println(this.getClass().getSimpleName() + "--" + this + "--" + s);
    }
}

在懶漢式的基礎(chǔ)上,對getsInstance加了同步,解決線程安全問題,但是大部分情況下,用不到同步,影響效率,所以不建議這種方式。

雙重鎖(DCL)

package com.qicode.kakaxicm.designpattern.singleton;

/**
 * Created by chenming on 2018/6/11
 */
public class DCLSingleton {
    private static DCLSingleton sInstance;

    private DCLSingleton() {

    }

    public static DCLSingleton getsInstance() {
        if (sInstance == null) {
            synchronized (DCLSingleton.class) {
                if(sInstance == null){
                    sInstance = new DCLSingleton();
                }
            }
        }
        return sInstance;
    }

    public void showMsg(String s) {
        System.out.println(this.getClass().getSimpleName() + "--" + this + "--" + s);
    }
}

第一次判空是為了避免不必要的同步,第二次的判空和構(gòu)建實例為原子操作,解決了多線程安全問題。這種方法只在第一次使用時稍慢,整體效率高,也解決了線程安全問題。不過還有比這更好的方法,那就是靜態(tài)內(nèi)部類。

靜態(tài)內(nèi)部類單例模式

package com.qicode.kakaxicm.designpattern.singleton;


/**
 * Created by chenming on 2018/6/11
 */
public class Singleton {
    private Singleton(){

    }

    private static class SingletonHolder{
        private static final Singleton sInstance = new Singleton();
    }

    public static Singleton getInstance(){
        return SingletonHolder.sInstance;
    }

    public void showMsg(String s) {
        System.out.println(this.getClass().getSimpleName() + "--" + this + "--" + s);
    }
}

靜態(tài)內(nèi)部類在Singleton加載時,并不會加載SingletonHolder類,因此實現(xiàn)懶加載,只有g(shù)etInstance被調(diào)用時,SingletonHolder才會被加載,此時實例才被創(chuàng)建,而且因為類只被加載一次,所以也不存在線程安全問題。

枚舉法

public enum Singleton {  
    INSTANCE;  
    public void showMsg(String s) {
        System.out.println(this.getClass().getSimpleName() + "--" + this + "--" + s);
    }
}  

枚舉實例的創(chuàng)建線程安全,并且在任何情況下都是單例。枚舉雖然簡單,但是因為可讀性的原因沒被推廣開來。

關(guān)于反射和反序列化的單例破壞問題

1.反射可以強(qiáng)制獲取構(gòu)造器,創(chuàng)建實例,可以在構(gòu)造器里面添加標(biāo)記,當(dāng)?shù)诙伪徽{(diào)用時拋出異常解決。
2.反序列化可可以創(chuàng)建新的對象,它提供了readResolve可以控制對象的反序列化:

private Object readResolve(){
return instance;
}

最后,說明一下這些方式的應(yīng)用場景。一般情況下,不建議使用第 1 種和第 2 種懶漢方式,建議使用第 3 種餓漢方式。只有在要明確實現(xiàn) lazy loading 效果時,才會使用第 5 種登記方式。如果涉及到反序列化創(chuàng)建對象時,可以嘗試使用第 6 種枚舉方式。如果有其他特殊的需求,可以考慮使用第 4 種雙檢鎖方式。
完整代碼地址:設(shè)計模式學(xué)習(xí)GayHub地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 開篇廢話 在寫這篇博客之前,我一直以為單例模式是最簡單的設(shè)計模式,哼,我還是太天真了??偨Y(jié)一下學(xué)習(xí)收獲吧。 什么是...
    IT廢柴閱讀 246評論 0 0
  • 單例模式:主要是為了避免因為創(chuàng)建了多個實例造成資源的浪費(當(dāng)創(chuàng)建對象時資源消耗較大的類),且多個實例由于多次調(diào)用容...
    葉明_b6b8閱讀 338評論 0 0
  • 單例模式(SingletonPattern)一般被認(rèn)為是最簡單、最易理解的設(shè)計模式,也因為它的簡潔易懂,是項目中最...
    成熱了閱讀 4,545評論 4 34
  • 1 單例模式的動機(jī) 對于一個軟件系統(tǒng)的某些類而言,我們無須創(chuàng)建多個實例。舉個大家都熟知的例子——Windows任務(wù)...
    justCode_閱讀 1,559評論 2 9
  • 夜已經(jīng)深了光腳的人兒走到窗前月光撒在他的身上屋外是一片荊棘光腳的人兒打開窗戶那只是荊棘鋪成的道路月光逐漸遠(yuǎn)了窗戶看...
    艾黑丫閱讀 339評論 5 15

友情鏈接更多精彩內(nèi)容