設(shè)計模式學(xué)習(xí)--單例模式

單例模式動機

對于系統(tǒng)中某些類來說,只有一個實例是很重要的,例如,一個系統(tǒng)中可以存在多個打印任務(wù),但只能有一個正在工作的任務(wù);一個系統(tǒng)只能有一個窗口管理器或文件系統(tǒng)。因此有時確保系統(tǒng)中某個對象的唯一性即一個類只能有一個實例非常重要。
如何保證一個類只有一個實例并且這個實例易于被訪問呢?定義一個全局變量可以確保對象隨時都可以被訪問,但不能防止我們實例化多個對象。一個更好的解決辦法就是讓類自身負(fù)責(zé)保存它的唯一實例。這個類可以保證沒有其他實例被創(chuàng)建,并且它提供一個訪問該實例的方法。這就是單例模式的動機。

單例模式定義

單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例,這個類成為單例類,它提供全局訪問的方法。
單例模式的要點有三個:一是某個類只能有一個實例;二是它必須自行創(chuàng)建這個實例;三是它必須自行向整個系統(tǒng)提供這個實例。
單例模式是一種對象創(chuàng)建模式,又名單件模式或單態(tài)模式。


單例模式結(jié)構(gòu)圖

單例模式只包含一個Singleton(單例角色):在單例類的內(nèi)部實現(xiàn)只生成一個實例,同時它提供一個靜態(tài)的getInstance()工廠方法,讓客戶可以使用它的唯一實例;為了防止在外部對其實例化,將其構(gòu)造函數(shù)設(shè)計為似有;在單例類內(nèi)部定義了一個Singletong類型的靜態(tài)對象,作為外部共享的唯一實例。

模式分析

單例模式的目的是保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。

public class Singleton {
    private static Singleton instance=null;//靜態(tài)私有成員變量
    //私有構(gòu)造函數(shù)
    private Singleton(){}
    //靜態(tài)工廠方法,返回唯一實例
    public static Singleton getInstance(){
        if(instance==null)
            instance=new Singleton();
        return instance;
    }
}

為了測試單例類所創(chuàng)建對象的唯一性,可以編寫如下客戶端測試代碼:

public class ClientTest {
    public static void main(String args[]){
        Singleton s1=Singleton.getInstance();
        Singleton s2=Singleton.getInstance();
        System.out.println(s1==s2);
    }
}

輸出結(jié)果:

true

在單例模式的實現(xiàn)過程中,需要注意一下三點:
1.單例類的構(gòu)造模式為私有
2.提供一個自身的靜態(tài)私有成員變量
3.提供一個公有的靜態(tài)工廠方法

單例模式實例與解析

1.實例說明
在現(xiàn)實生活中,居民身份證具有唯一性,同一個人不允許有多個身份證號碼,第一次申請身份證將給居民分配一個身份證號碼,如果之后因為遺失等原因補辦時,還是使用原來的身份證號碼,不會產(chǎn)生新的號碼。
2.實例類圖


身份證號碼類圖

3.實例代碼及解釋
單例類IdentityCardNo如下:

public class IdentityCardNo {
    private static IdentityCardNo instance=null;
    private String no;
    
    private IdentityCardNo(){}
    
    public static IdentityCardNo getInstance(){
        if(instance==null){
            System.out.println("第一次辦理身份證,分配新號碼");
            instance=new IdentityCardNo();
            instance.setIdentityCardNo("No123456789");
        }else{
            System.out.println("重復(fù)辦理身份證,獲取舊號碼");
        }
        return instance;
    }

    private void setIdentityCardNo(String no) {
        this.no=no;
    }
    private void getIdentityCardNo() {
        this.no=no;
    }
}

客戶端代碼如下:

public class ClientTest {
    public static void main(String a[]){
        IdentityCardNo no1,no2;
        no1=IdentityCardNo.getInstance();
        no2=IdentityCardNo.getInstance();
        System.out.println("身份證號碼是否一致: "+ (no1==no2));
        
        String str1,str2;
        str1=no1.getIdentityCardNo();
        str2=no2.getIdentityCardNo();
        System.out.println("第一次號碼:"+str1);
        System.out.println("第二次號碼:"+str2);
        System.out.println("內(nèi)容是否相等:"+str1.equals(str2));
        System.out.println("是否是相同對象:"+(str1==str2));
    }
}

運行結(jié)果如下:

第一次辦理身份證,分配新號碼
重復(fù)辦理身份證,獲取舊號碼
身份證號碼是否一致: true
第一次號碼:No123456789
第二次號碼:No123456789
內(nèi)容是否相等:true
是否是相同對象:true

從結(jié)果可以看出,兩次創(chuàng)建的IdentityCardNo對象內(nèi)存地址相同,是同一個對象;封裝在其中的號碼no屬性不僅值相等,而且內(nèi)存地址也相等,是同一個成員屬性。

單例模式優(yōu)缺點

優(yōu)點:
(1)提供了 對唯一實例的受控訪問。因為單例類封裝了它的唯一實例,所以它可以嚴(yán)格控制客戶怎樣以及何時訪問它。
(2)由于在系統(tǒng)中只存在一個對象,因此可以節(jié)約系統(tǒng)資源,對于一些需要頻繁創(chuàng)建和銷毀的對象,單例模式無疑可以提高系統(tǒng)的性能
(3)允許可變數(shù)目的實例。基于單例模式我們可以進(jìn)行擴展,使用與單例控制相似的方法來獲得指定個數(shù)的對象實例。
缺點:
(1)由于單例模式?jīng)]有抽象層,因此單例類的擴展有很大的困難
(2)單例類的職責(zé)過重,在一定程度上違背了“單一職責(zé)原則”。因為單例類既充當(dāng)了工廠角色,提供了工廠方法,同時又充當(dāng)了產(chǎn)品角色,包含一些業(yè)務(wù)方法,將產(chǎn)品的創(chuàng)建和產(chǎn)品本身的功能融合到一起。
(3)濫用單例將帶來一些負(fù)面問題,如為了節(jié)省資源將數(shù)據(jù)庫連接池對象設(shè)計成單例類,可能導(dǎo)致共享連接池對象的程序過多而出現(xiàn)連接池溢出。等。

模式適用環(huán)境

1.系統(tǒng)只需要一個實例對象,如系統(tǒng)要求提供一個唯一的序列號生成器,或者需要考慮資源消耗太大而只允許創(chuàng)建一個對象。
2.客戶調(diào)用類的單個實例只允許使用一個公共訪問點,除了該公共訪問點,不能通過其他途徑訪問該實例。

單例模式擴展

(1)餓漢式單例類:在定義靜態(tài)變量時實例化單例類,因此在類加載的時候就已經(jīng)創(chuàng)建了單例對象。

public class EagerSingleton {
    private static final EagerSingleton instance=null;
    private EagerSingleton(){}
    public static EagerSingleton getInstance(){
        return instance;
    }
}

在這個類被加載時,靜態(tài)變量instance會被初始化,此時類的私有構(gòu)造函數(shù)會被調(diào)用,單例類的唯一實例將被創(chuàng)建。Java語言中單例類的一個重要的特點是類的構(gòu)造函數(shù)是私有的,從而避免外界利用構(gòu)造函數(shù)直接創(chuàng)建出任意多的實例。
(2)懶漢式單例類:與餓漢式單例類相同之處是,懶漢式單例類的構(gòu)造函數(shù)也是私有的。與餓漢式單例類不同的是,懶漢式單例類在第一次被引用時將自己實例化,在懶漢式單例類被加載時不會講自己實例化。

public class LazySingleton {
    private static LazySingleton instance=null;
    private LazySingleton(){}
    synchronized public static LazySingleton getInstance(){
        if(instance==null){
            instance=new LazySingleton();
        }
            return instance;
    }
}
?著作權(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)容

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