單例模式

目錄

1、設(shè)計(jì)模式簡(jiǎn)介

2、什么是單例模式

3、單例模式應(yīng)用場(chǎng)合

4、單例模式作用

5、單例模式3個(gè)要點(diǎn)/要素

6、單例模式的實(shí)現(xiàn)思路

7、單利模式的實(shí)現(xiàn)原則和過(guò)程

8、單例模式有三種實(shí)現(xiàn)方式

9、怎么區(qū)分餓漢和懶漢模式?

10、單例模式需要注意的地方

11、單例模式的優(yōu)點(diǎn)

12、單例模式的缺點(diǎn)

參考資料:

https://www.imooc.com/video/1772

https://www.cnblogs.com/damsoft/p/6105122.html

單例模式實(shí)踐代碼


1、設(shè)計(jì)模式簡(jiǎn)介

設(shè)計(jì)模式是前輩們?cè)诙嗄觊_(kāi)發(fā)工作中經(jīng)驗(yàn)的總結(jié),可以提高代碼的可重用性、可靠性和規(guī)范性,讓代碼更容易理解,而單例模式是在Java中最重要、最簡(jiǎn)單、最常用的設(shè)計(jì)模式之一。

2、什么是單例模式

定義一

確保一個(gè)類(lèi)之后一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例,這個(gè)類(lèi)稱(chēng)為單例類(lèi),提供了全局訪(fǎng)問(wèn)的方法。單例模式是一種對(duì)象創(chuàng)建型模式。




定義二

單例模式(Singleton),也叫單子模式,是一種常用的軟件設(shè)計(jì)模式。在應(yīng)用這個(gè)模式時(shí),單例對(duì)象的類(lèi)必須保證只有一個(gè)實(shí)例存在。



3、單例模式應(yīng)用場(chǎng)合

單例模式只允許創(chuàng)建一個(gè)對(duì)象,因此節(jié)省內(nèi)存,加快對(duì)象訪(fǎng)問(wèn)速度,因此對(duì)象需要被公用的場(chǎng)合適合使用,如多個(gè)模塊使用同一個(gè)數(shù)據(jù)源連接對(duì)象等等。如:

(1)需要頻繁實(shí)例化然后銷(xiāo)毀的對(duì)象。

(2)創(chuàng)建對(duì)象時(shí)耗時(shí)過(guò)多或者耗資源過(guò)多,但又經(jīng)常用到的對(duì)象。

(3)有狀態(tài)的工具類(lèi)對(duì)象。

(4)頻繁訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)或文件的對(duì)象。

以下都是單例模式的經(jīng)典使用場(chǎng)景:

(1)資源共享的情況下,避免由于資源操作時(shí)導(dǎo)致的性能或損耗等。如上述中的日志文件,應(yīng)用配置。

(2)控制資源的情況下,方便資源之間的互相通信。如線(xiàn)程池等。

應(yīng)用場(chǎng)景舉例:

1>.外部資源:每臺(tái)計(jì)算機(jī)有若干個(gè)打印機(jī),但只能有一個(gè)PrinterSpooler,以避免兩個(gè)打印作業(yè)同時(shí)輸出到打印機(jī)。內(nèi)部資源:大多數(shù)軟件都有一個(gè)(或多個(gè))屬性文件存放系統(tǒng)配置,這樣的系統(tǒng)應(yīng)該有一個(gè)對(duì)象管理這些屬性文件


2>. Windows的Task Manager(任務(wù)管理器)就是很典型的單例模式(這個(gè)很熟悉吧),想想看,是不是呢,你能打開(kāi)兩個(gè)windows task manager嗎? 不信你自己試試看哦~


3>. windows的RecycleBin(回收站)也是典型的單例應(yīng)用。在整個(gè)系統(tǒng)運(yùn)行過(guò)程中,回收站一直維護(hù)著僅有的一個(gè)實(shí)例。


4>. 網(wǎng)站的計(jì)數(shù)器,一般也是采用單例模式實(shí)現(xiàn),否則難以同步。


5>. 應(yīng)用程序的日志應(yīng)用,一般都何用單例模式實(shí)現(xiàn),這一般是由于共享的日志文件一直處于打開(kāi)狀態(tài),因?yàn)橹荒苡幸粋€(gè)實(shí)例去操作,否則內(nèi)容不好追加。


6>. Web應(yīng)用的配置對(duì)象的讀取,一般也應(yīng)用單例模式,這個(gè)是由于配置文件是共享的資源。


7>. 數(shù)據(jù)庫(kù)連接池的設(shè)計(jì)一般也是采用單例模式,因?yàn)閿?shù)據(jù)庫(kù)連接是一種數(shù)據(jù)庫(kù)資源。數(shù)據(jù)庫(kù)軟件系統(tǒng)中使用數(shù)據(jù)庫(kù)連接池,主要是節(jié)省打開(kāi)或者關(guān)閉數(shù)據(jù)庫(kù)連接所引起的效率損耗,這種效率上的損耗還是非常昂貴的,因?yàn)楹斡脝卫J絹?lái)維護(hù),就可以大大降低這種損耗。


8>. 多線(xiàn)程的線(xiàn)程池的設(shè)計(jì)一般也是采用單例模式,這是由于線(xiàn)程池要方便對(duì)池中的線(xiàn)程進(jìn)行控制。


9>. 操作系統(tǒng)的文件系統(tǒng),也是大的單例模式實(shí)現(xiàn)的具體例子,一個(gè)操作系統(tǒng)只能有一個(gè)文件系統(tǒng)。


10>. HttpApplication 也是單位例的典型應(yīng)用。熟悉ASP.Net(IIS)的整個(gè)請(qǐng)求生命周期的人應(yīng)該知道HttpApplication也是單例模式,所有的HttpModule都共享一個(gè)HttpApplication實(shí)例.

4、單例模式作用

保證整個(gè)應(yīng)用程序中某個(gè)實(shí)例有且只有一個(gè);

5、單例模式3個(gè)要點(diǎn)/要素

1.某個(gè)類(lèi)只能有一個(gè)實(shí)例

2.它必須自行創(chuàng)建這個(gè)實(shí)例

3.它必須自行向整個(gè)系統(tǒng)提供這個(gè)實(shí)例

換一種說(shuō)法:

a.私有構(gòu)造方法

b.私有靜態(tài)引用指向自己實(shí)例

c.以自己實(shí)例為返回值的公有靜態(tài)方法

6、單例模式的實(shí)現(xiàn)思路

一個(gè)類(lèi)能返回對(duì)象一個(gè)引用(永遠(yuǎn)是同一個(gè))和一個(gè)獲得該實(shí)例的方法(必須是靜態(tài)方法,通常使用getInstance這個(gè)名 稱(chēng));

當(dāng)我們調(diào)用這個(gè)方法時(shí),如果類(lèi)持有的引用不為空就返回這個(gè)引用,如果類(lèi)保持的引用為空就創(chuàng)建該類(lèi)的實(shí)例并將實(shí)例的引用賦予該類(lèi)保持的引用;

同時(shí)我們還將該類(lèi)的構(gòu)造函數(shù)定義為私有方法,這樣其他處的代碼就無(wú)法通過(guò)調(diào)用該類(lèi)的構(gòu)造函數(shù)來(lái)實(shí)例化該類(lèi)的對(duì)象,只有通過(guò)該類(lèi)提供的靜態(tài)方法來(lái)得到該類(lèi)的唯一實(shí)例。

7、單利模式的實(shí)現(xiàn)原則和過(guò)程

(1)單例模式:確保一個(gè)類(lèi)只有一個(gè)實(shí)例,自行實(shí)例化并向系統(tǒng)提供這個(gè)實(shí)例

(2)單例模式分類(lèi):

餓單例模式(類(lèi)加載時(shí)實(shí)例化一個(gè)對(duì)象給自己的引用);


懶單例模式(調(diào)用取得實(shí)例的方法如getInstance時(shí)才會(huì)實(shí)例化對(duì)象)(java中餓單例模式性能優(yōu)于懶單例模式,c++中一般使用懶單例模式)

8、單例模式有三種實(shí)現(xiàn)方式

1.餓漢式單例(Eager Singleton)

在類(lèi)初始化的時(shí)候,創(chuàng)建對(duì)象,這種方式是線(xiàn)程安全的,在程序運(yùn)行期間就這一個(gè)對(duì)象。

2.懶漢式單例(Layz Singleton)

懶漢式是在第一次使用時(shí)才創(chuàng)建對(duì)象,但是如果在多線(xiàn)程環(huán)境中要考慮線(xiàn)程安全問(wèn)題。

3.Initialization on Demand Holder(IoDH) 技術(shù)(注:有的面向?qū)ο笳Z(yǔ)言不支持)



懶漢模式代碼Demo

/**
 * @ClassName LazySingleton  <br>
 * @Description 懶漢模式
 **/
public class LazySingleton {
    //1.將構(gòu)造方式私有化,不允許外邊直接創(chuàng)建對(duì)象
    private LazySingleton() {
    }

    //2.聲明類(lèi)的唯一實(shí)例,使用private static修飾
    private static LazySingleton lazySingletonInstance;

    //只是聲明了類(lèi)的實(shí)例,并沒(méi)有實(shí)例化,如果外面通過(guò)getInstance()來(lái)獲取的時(shí)候,明顯該實(shí)例是空的——》咋辦呢?——》getLazySingletonInstance()
    /**
     * 3.提供一個(gè)用于獲取實(shí)例的方法,使用public static修飾
     * Description: 當(dāng)用戶(hù)獲取類(lèi)的實(shí)例時(shí),做一個(gè)判斷,若實(shí)例為空則創(chuàng)建一個(gè)實(shí)例,若不為空則直接返回實(shí)例:
     */
    public static LazySingleton getLazySingletonInstance() {
        if (lazySingletonInstance == null) {
            lazySingletonInstance = new LazySingleton();
        }
        return lazySingletonInstance;
    }
}

其他代碼略,如有需要請(qǐng)至:單例模式實(shí)踐代碼

測(cè)試代碼
注:測(cè)試代碼中可以看到餓漢模式

/**
 * @ClassName SingletonTest  <br>
 * @Description 無(wú)論獲取多少次Singleton,他們實(shí)際上都是一個(gè)實(shí)例,指向同一個(gè)對(duì)象,這樣就保證了單個(gè)實(shí)例
 * 為了安全,控制訪(fǎng)問(wèn),不允許外部直接訪(fǎng)問(wèn)類(lèi)的成員變量:
 **/

public class SingletonTest {
    public static void main(String[] args) {
//        ****** 餓漢模式start ******
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        //獲取2個(gè)實(shí)例后,想看看這兩個(gè)實(shí)例是否是同一個(gè)實(shí)例
        if (singleton1 == singleton2) {
            System.out.println("singleton1,singleton2是同一個(gè)實(shí)例");
        } else {
            System.out.println("singleton1,singleton2不是同一個(gè)實(shí)例");
        }
//        ****** 餓漢模式end ******

//        ****** 懶漢模式start ******
        LazySingleton lazySingleton1 = LazySingleton.getLazySingletonInstance();
        LazySingleton lazySingleton2 = LazySingleton.getLazySingletonInstance();
        if (lazySingleton1 == lazySingleton2) {
            System.out.println("lazySingleton1,lazySingleton2是同一個(gè)實(shí)例");
        } else {
            System.out.println("lazySingleton1,lazySingleton2不是同一個(gè)實(shí)例");
        }
//        ****** 懶漢模式end ******
    }
}

測(cè)試結(jié)果:

singleton1,singleton2是同一個(gè)實(shí)例
lazySingleton1,lazySingleton2是同一個(gè)實(shí)例

9、怎么區(qū)分餓漢和懶漢模式?

餓漢:餓了,饑不擇食,系統(tǒng)一運(yùn)行就創(chuàng)建對(duì)象,不考慮太多的問(wèn)題

懶漢:就是懶,就是不想創(chuàng)建對(duì)象,即使運(yùn)行創(chuàng)建對(duì)象,也要說(shuō)一大堆的問(wèn)題,什么多線(xiàn)程、安全,對(duì)象重復(fù)等問(wèn)題。總之一句話(huà):懶,就是不想創(chuàng)建。

10、單例模式需要注意的地方

單例模式在多線(xiàn)程的應(yīng)用場(chǎng)合下必須小心使用。如果當(dāng)唯一實(shí)例尚未創(chuàng)建時(shí),有兩個(gè)線(xiàn)程同時(shí)調(diào)用創(chuàng)建方法,那么它們同時(shí)沒(méi)有檢測(cè)到唯一實(shí)例的存在,從而同時(shí)各自創(chuàng)建了一個(gè)實(shí)例, 這樣就有兩個(gè)實(shí)例被構(gòu)造出來(lái),從而違反了單例模式中實(shí)例唯一的原則。



解決這個(gè)問(wèn)題的辦法是為指示類(lèi)是否已經(jīng)實(shí)例化的變量提供一個(gè)互斥鎖(雖然這樣會(huì)降低效率)。

(1)使用時(shí)不能用反射模式創(chuàng)建單例,否則會(huì)實(shí)例化一個(gè)新的對(duì)象

(2)使用懶單例模式時(shí)注意線(xiàn)程安全問(wèn)題

(3)餓單例模式和懶單例模式構(gòu)造方法都是私有的,因而是不能被繼承的,有些單例模式可以被繼承(如登記式模式)

11、單例模式的優(yōu)點(diǎn)

(1)在單例模式中,活動(dòng)的單例只有一個(gè)實(shí)例,對(duì)單例類(lèi)的所有實(shí)例化得到的都是相同的一個(gè)實(shí)例。這樣就 防止其它對(duì)象對(duì)自己的實(shí)例化,確保所有的對(duì)象都訪(fǎng)問(wèn)一個(gè)實(shí)例。


(2)單例模式具有一定的伸縮性,類(lèi)自己來(lái)控制實(shí)例化進(jìn)程,類(lèi)就在改變實(shí)例化進(jìn)程上有相應(yīng)的伸縮性。


(3)提供了對(duì)唯一實(shí)例的受控訪(fǎng)問(wèn)。


(4)由于在系統(tǒng)內(nèi)存中只存在一個(gè)對(duì)象,因此可以節(jié)約系統(tǒng)資源,當(dāng)需要頻繁創(chuàng)建和銷(xiāo)毀的對(duì)象時(shí)單例模式無(wú)疑可以提高系統(tǒng)的性能。


(5)允許可變數(shù)目的實(shí)例。

(6)避免對(duì)共享資源的多重占用。

12、單例模式的缺點(diǎn)

(1)不適用于變化的對(duì)象,如果同一類(lèi)型的對(duì)象總是要在不同的用例場(chǎng)景發(fā)生變化,單例就會(huì)引起數(shù)據(jù)的錯(cuò)誤,不能保存彼此的狀態(tài)。

(2)由于單利模式中沒(méi)有抽象層,因此單例類(lèi)的擴(kuò)展有很大的困難。

(3)單例類(lèi)的職責(zé)過(guò)重,在一定程度上違背了“單一職責(zé)原則”。

(4)濫用單例將帶來(lái)一些負(fù)面問(wèn)題,如為了節(jié)省資源將數(shù)據(jù)庫(kù)連接池對(duì)象設(shè)計(jì)為的單例類(lèi),可能會(huì)導(dǎo)致共享連接池對(duì)象的程序過(guò)多而出現(xiàn)連接池溢出;如果實(shí)例化的對(duì)象長(zhǎng)時(shí)間不被利用,系統(tǒng)會(huì)認(rèn)為是垃圾而被回收,這將導(dǎo)致對(duì)象狀態(tài)的丟失。

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

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

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