每天一個設計模式-4 單例模式(Singleton)

来源:http://www.cnblogs.com/xiemubg/archive/2016/10/13/5954949.html
-Advertisement-
Play Games

每天一個設計模式-4 單例模式(Singleton) 1.實際生活的例子 有一天,你的自行車的某個螺絲釘鬆了,修車鋪離你家比較遠,而附近的五金店有賣扳手;因此,你決定去五金店買一個扳手,自己把螺絲釘固定緊。不一會兒,自行車就被你修好了;首先,這個扳手你不會扔掉,下次用的時候直接找出來就用了。好,今天 ...


每天一個設計模式-4 單例模式(Singleton)

1.實際生活的例子

有一天,你的自行車的某個螺絲釘鬆了,修車鋪離你家比較遠,而附近的五金店有賣扳手;因此,你決定去五金店買一個扳手,自己把螺絲釘固定緊。不一會兒,自行車就被你修好了;首先,這個扳手你不會扔掉,下次用的時候直接找出來就用了。好,今天的主題出來了“單例模式”。

2.與變成關聯

在上面的例子中,能找出幾個關鍵字:“買一個扳手”,“螺絲釘固定緊”,“不會扔掉扳手”,“下次用直接找出來”。我們結合標題和這幾個關鍵字好好理解一下:

買一個扳手:創建一個實例。

螺絲釘固定緊:這個實例的功能。

不會扔掉扳手:保存這個實例。

下次用直接找出來:從保存的實例中取出來。

ok,今天我們學習的重點就是這些。

3.單例模式的定義

保證一個類僅有一個實例,並提供一個訪問它的全局訪問點

分析:“僅有一個實例”,如何做到僅有一個實例呢?我們知道,java中實例的生成需要通過構造函數,外部通過這個構造函數生成對應的對象,那麼如果我們把構造函數私有化不就可以做到外部無法實例化該對象了嗎;但是,該如何調用這個實例的方法呢?我們可以在該類內部執行構造函數,並將構造後的對象保存在該類的屬性中,並提供一個全局的訪問點供外部調用,因為不能在外部生成該類實例,所以這個全局訪問點被static修飾,這樣我們就可以直接通過類訪問這個方法。

好的!!下麵放出代碼。

4.類圖

4.代碼

實現單利模式,有兩種方法,一種是懶漢式,另一種是餓漢式,最終都是單例模式,只是實現方式略有不同。下麵先放出代碼:

懶漢式實現

public class BanShou {

    private static BanShou savedBanShou=null;



    public void finalize() throws Throwable {

    }

    private BanShou(){
        System.out.println("我買了一個扳手");
    }
    
    public static BanShou getInstance(){
        if(savedBanShou==null){
            savedBanShou = new BanShou();
            return savedBanShou;
        }
        System.out.println("嘿,我找到了我以前買的扳手,這下可以不用再買了");
        return savedBanShou;
    }

    public void repaire(){
        System.out.println("擰緊螺絲釘");
    }

}
扳手類
public class Client {

    public static void main(String[] args) {
        for(int i=0;i<3;i++){
            System.out.println("這是第"+i+"次用到了扳手");
            BanShou.getInstance().repaire();;
        }
    }
}
客戶類

餓漢式實現:

public class BanShou2 {
    private static BanShou2 savedBanShou=new BanShou2();



    public void finalize() throws Throwable {

    }

    private BanShou2(){
        System.out.println("我買了一個扳手");
    }
    
    public static BanShou2 getInstance(){
            return savedBanShou;
    }

    public void repaire(){
        System.out.println("擰緊螺絲釘");
    }
}
餓漢式扳手類

測試結果:

這是第0次用到了扳手
我買了一個扳手
擰緊螺絲釘
這是第1次用到了扳手
嘿,我找到了我以前買的扳手,這下可以不用再買了
擰緊螺絲釘
這是第2次用到了扳手
嘿,我找到了我以前買的扳手,這下可以不用再買了
擰緊螺絲釘

5.模式講解

在上面的代碼中看到單例模式可以用兩種方式實現,那麼他們的區別是什麼呢?首先,先從名稱上理解一下,懶漢式:因為很懶,所以做事都是拖到最後,不得不做時才去做;餓漢式:因為很餓,所以做事很急。體會一下應該就能懂了。↓↓↓↓懶漢式

public static BanShou getInstance(){
        if(savedBanShou==null){
            savedBanShou = new BanShou();
            return savedBanShou;
        }
        System.out.println("嘿,我找到了我以前買的扳手,這下可以不用再買了");
        return savedBanShou;
    }

獲取實例時,先去判斷是否為空,如果為空,再去創建(拖到最後,不得不做時才去做)。

↓↓↓↓餓漢式

private static BanShou2 savedBanShou=new BanShou2();
public static  BanShou2 getInstance(){
            return savedBanShou;
    }

獲取實例前就已經創建好了,無論你用不用(做事很急)。

懶漢式實現方式中也涉及了延遲載入的思想(LazyLoad),通俗的說就是:一開始時不要載入資源或數據,一直等,等到馬上就要使用這個資源或者數據了,躲不過了才去載入,這在實際開發中是一種常見的思想,儘可能的節約資源。

餓漢式和懶漢式的實現還體現了緩存的思想:當某個文件,或資料庫數據被頻繁使用,如果每次都從文件或資料庫中讀取,速度會變得很慢,如果把這些數據放到記憶體中,每次取數據時都從緩存中提取,那麼速度便會得到很大的提升。

private static BanShou2 savedBanShou=new BanShou2();
private static BanShou savedBanShou=null;

這兩行代碼就是起到了緩存的作用。

6.單利模式的線程問題

懶漢式實現方式是線程不安全的,比如:

如何解決呢?想讓懶漢式變成線程安全的,最簡單的辦法就是用synchronized

public static synchronized BanShou getInstance()

但這樣做會降低系統性能,因為線程互斥,每次都要等待。

有一種雙重加鎖方式可以解決這個問題。雙重加鎖方式就是:

並不是每次進入getInstance方法都需要同步,而是先不同步,進入方法過後,先檢查實例是否存在,如果不存在才進入下麵的同步塊,這是第一重檢查。進入同步塊過後,再次檢查實例是否存在,如果不存在,就在同步的情況下創建實例,這是第二重檢查。這樣,就只需要同步一次,從而減少了多次在同步情況下進行判斷所浪費的時間。雙重加鎖機制的實現會使用一個關鍵字volatile,它的意思是:被volatile修飾的變數的值,將不會被本地線程緩存,所有對該變數的讀寫都是直接操作共用記憶體,從而確保多個線程能正確的處理該變數。

但這種方式用到了volatile,它會屏蔽掉虛擬機中的一些必要的代碼優化,所以運行效率不是很高,下麵推薦一個更好的單例實現方式:

直接放出源代碼吧,也涉及一些其他的基礎知識,這裡就不寫了,畢竟這麼晚了;今天比較忙,所以現在才更新。不好意了。

更好的單例模式實現源代碼:

public class Singleton {
    //這部分只有被調用到時,才會被裝載,既線程安全,有延遲載入
    private static class SingletonHolder{
        private static Singleton instance = new Singleton();
    }
    private Singleton(){
        
    }
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

7.總結

實現單例模式,就必須將構造器私有化,在類內部實例化,並提供全局的訪問點,從而達到控制實例的數目的目的

 

 

-------博主寫博客不容易,轉載請註明出處,謝謝:http://www.cnblogs.com/xiemubg/p/5954949.html

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 【Python練習題 007】 有一對兔子,從出生後第3個月起每個月都生一對兔子,小兔子長到第三個月後每個月又生一對兔子,假如兔子都不死,問每個月的兔子總數為多少? 這題反正我自己是算不出來。後來搜索了網上,說是經典的“斐波納契數列”。於是我自己排畫了一下(如下圖,小寫表示小兔子,大寫表示大兔子): ...
  • 敵兵佈陣 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 79267 Accepted Submission(s): 33532 Problem ...
  • A集成代碼生成器 [正反雙向(單表、主表、明細表、樹形表,開發利器)+快速構建表單 下載地址 ; freemaker模版技術 ,0個代碼不用寫,生成完整的一個模塊,帶頁面、建表sql腳本,處理類,service等完整模塊B 集成阿裡巴巴資料庫連接池druid; 資料庫連接池 阿裡巴巴的 druid。 ...
  • 本文主要是探究學習比較流行的一款消息層是如何設計與實現的 ØMQ是一種消息傳遞系統,或者樂意的話可以稱它為“面向消息的中間件”。它在金融服務,游戲開發,嵌入式系統,學術研究和航空航天等多種環境中被使用。 消息傳遞系統基本上像應用程式的即時消息一樣工作。應用程式決定將事件傳送到另一個應用程式(或多個應 ...
  • 每天一個設計模式-5 工廠方法模式 1.模式定義 定義一個用於創建對象的介面,讓子類決定實例化那一個類,Factory Method使一個類的實例化延遲到其子類。 2.工廠方法模式解決問題的思路 工廠方法模式需要介面對象,那就定義一個方法來創建這個介面對象(工廠方法);可是事實上它自己是不知道如何創 ...
  • 使用的lib庫有: 在lib庫目錄下新建一個src文件夾用來存放生成的文件,然後新建generatorConfig.xml 裡面代碼為: 最後在cmd控制臺下找到lib的根目錄然後執行以下語句 1 Java -jar mybatis-generator-core-1.3.5.jar -configf ...
  • 參考:github, https://github.com/liuxiaochen0625/MyBatis-Spring-Boot-master.git 從controller組裝tk.mybatis.mapper.entity.Example 對象,操作起來較為麻煩,不符合我們日常書寫習慣,因而改 ...
  • 這是一個好的開始,過程很漫長,但我卻樂在其中. 在大學之際,這是我的又一個開始,隨便寫點啦. 想把每一次的過程記錄下來 這樣以後對自己 對別人都會有所幫助。 好啦 作為一名大三的學生黨 加油吧! ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...