從實例出發,瞭解單例模式和靜態塊

来源:https://www.cnblogs.com/fengzheng/archive/2018/05/29/9103227.html
-Advertisement-
Play Games

就算你沒有用到過其他的設計模式,但是單例模式你肯定接觸過,比如,Spring 中 bean 預設就是單例模式的,所有用到這個 bean 的實例其實都是同一個。 單例模式的使用場景 什麼是單例模式呢,單例模式(Singleton)又叫單態模式,它出現目的是為了保證一個類在系統中只有一個實例,並提供一個 ...


就算你沒有用到過其他的設計模式,但是單例模式你肯定接觸過,比如,Spring 中 bean 預設就是單例模式的,所有用到這個 bean 的實例其實都是同一個。

單例模式的使用場景

什麼是單例模式呢,單例模式(Singleton)又叫單態模式,它出現目的是為了保證一個類在系統中只有一個實例,並提供一個訪問它的全局訪問點。從這點可以看出,單例模式的出現是為了可以保證系統中一個類只有一個實例而且該實例又易於外界訪問,從而方便對實例個數的控制並節約系統資源而出現的解決方案。

使用單例模式當然是有原因,有好處的了。在下麵幾個場景中適合使用單例模式:

1、有頻繁實例化然後銷毀的情況,也就是頻繁的 new 對象,可以考慮單例模式;

2、創建對象時耗時過多或者耗資源過多,但又經常用到的對象;

3、頻繁訪問 IO 資源的對象,例如資料庫連接池或訪問本地文件;

下麵舉幾個例子來說明一下:

1、網站線上人數統計;

其實就是全局計數器,也就是說所有用戶在相同的時刻獲取到的線上人數數量都是一致的。要實現這個需求,計數器就要全局唯一,也就正好可以用單例模式來實現。當然這裡不包括分散式場景,因為計數是存在記憶體中的,並且還要保證線程安全。下麵代碼是一個簡單的計數器實現。

public class Counter {
   
    private static class CounterHolder{
        private static final Counter counter = new Counter();
    }

    private Counter(){
        System.out.println("init...");
    }

    public static final Counter getInstance(){
        return CounterHolder.counter;
    }

    private AtomicLong online = new AtomicLong();

    public long getOnline(){
        return online.get();
    }

    public long add(){
        return online.incrementAndGet();
    }
}    

2、配置文件訪問類;

項目中經常需要一些環境相關的配置文件,比如簡訊通知相關的、郵件相關的。比如 properties 文件,這裡就以讀取一個properties 文件配置為例,如果你使用的 Spring ,可以用 @PropertySource 註解實現,預設就是單例模式。如果不用單例的話,每次都要 new 對象,每次都要重新讀一遍配置文件,很影響性能,如果用單例模式,則只需要讀取一遍就好了。以下是文件訪問單例類簡單實現:

public class SingleProperty {

    private static Properties prop;

    private static class SinglePropertyHolder{
        private static final SingleProperty singleProperty = new SingleProperty();
    }

    /**
    * config.properties 內容是 test.name=kite 
    */
    private SingleProperty(){
        System.out.println("構造函數執行");
        prop = new Properties();
        InputStream stream = SingleProperty.class.getClassLoader()
                .getResourceAsStream("config.properties");
        try {
            prop.load(new InputStreamReader(stream, "utf-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SingleProperty getInstance(){
        return SinglePropertyHolder.singleProperty;
    }
    
    
    public String getName(){
        return prop.get("test.name").toString();
    }

    public static void main(String[] args){
        SingleProperty singleProperty = SingleProperty.getInstance();
        System.out.println(singleProperty.getName());
    }
}

3、資料庫連接池的實現,也包括線程池。為什麼要做池化,是因為新建連接很耗時,如果每次新任務來了,都新建連接,那對性能的影響實在太大。所以一般的做法是在一個應用內維護一個連接池,這樣當任務進來時,如果有空閑連接,可以直接拿來用,省去了初始化的開銷。所以用單例模式,正好可以實現一個應用內只有一個線程池的存在,所有需要連接的任務,都要從這個連接池來獲取連接。如果不使用單例,那麼應用內就會出現多個連接池,那也就沒什麼意義了。如果你使用 Spring 的話,並集成了例如 druid 或者 c3p0 ,這些成熟開源的資料庫連接池,一般也都是預設以單例模式實現的。

單例模式的實現方法

如果你在書上或者網站上搜索單例模式的實現,一般都會介紹5、6中方式,其中有一些隨著 Java 版本的升高,以及多線程技術的使用變得不那麼實用了,這裡就介紹兩種即高效,而且又是線程安全的方式。

1. 靜態內部類方式

public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE; 
    }  
}

這種寫法仍然使用 JVM 本身機制保證了線程安全問題,由於 SingletonHolder 是私有的,除了 getInstance() 方法外沒有辦法訪問它,因此它是懶漢式的;同時讀取實例的時候不會進行同步,沒有性能缺陷;也不依賴 JDK 版本。上面的兩個例子就是用這種方式實現的。

2. 枚舉方式

public enum SingleEnum {

    INSTANCE;

    SingleEnum(){
        System.out.println("構造函數執行");
    }

    public String getName(){
        return "singleEnum";
    }

    public static void main(String[] args){
        SingleEnum singleEnum = SingleEnum.INSTANCE;
        System.out.println(singleEnum.getName());
    }
}

我們可以通過 SingleEnum.INSTANCE 來訪問實例。而且創建枚舉預設就是線程安全的,並且還能防止反序列化導致重新創建新的對象。

靜態塊

什麼是靜態塊呢。

1、它是隨著類的載入而執行,只執行一次,並優先於主函數。具體說,靜態代碼塊是由類調用的。類調用時,先執行靜態代碼塊,然後才執行主函數的;

2、靜態代碼塊其實就是給類初始化的,而構造代碼塊是給對象初始化的;

3、靜態代碼塊中的變數是局部變數,與普通函數中的局部變數性質沒有區別;

4、一個類中可以有多個靜態代碼塊;

他的寫法是這樣的:

static {
        System.out.println("static executed");
    }

來看一下下麵這個完整的實例:

public class SingleStatic {

    static {
        System.out.println("static 塊執行中...");
    }

    {
        System.out.println("構造代碼塊 執行中...");
    }

    public SingleStatic(){
        System.out.println("構造函數 執行中");
    }

    public static void main(String[] args){
        System.out.println("main 函數執行中");
        SingleStatic singleStatic = new SingleStatic();
    }

}

他的執行結果是這樣的:

static 塊執行中...
main 函數執行中
構造代碼塊 執行中...
構造函數 執行中

從中可以看出他們的執行順序分別為:

1、靜態代碼塊

2、main 函數

3、構造代碼塊

4、構造函數

利用靜態代碼塊只在類載入的時候執行,並且只執行一次這個特性,也可以用來實現單例模式,但是不是懶載入,也就是說每次類載入就會主動觸發實例化。

除此之外,不考慮單例的情況,利用靜態代碼塊的這個特性,可以實現其他的一些功能,例如上面提到的配置文件載入的功能,可以在類載入的時候就讀取配置文件的內容,相當於一個預載入的功能,在使用的時候可以直接拿來就用。

不妨拿出手機掃一下,關註我的微信公眾號,關註後可點擊“加群”加入微信交流群


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

-Advertisement-
Play Games
更多相關文章
  • 1.模型管理 :web線上流程設計器、預覽流程xml、導出xml、部署流程 2.流程管理 :導入導出流程資源文件、查看流程圖、根據流程實例反射出流程模型、激活掛起 3.運行中流程:查看流程信息、當前任務節點、當前流程圖、作廢暫停流程、指派待辦人 4.歷史的流程:查看流程信息、流程用時、流程狀態、查看 ...
  • 簡介: 動態的給一個對象添加一些額外的職責,就增加功能來說,裝飾模式比生產子類更加靈活——《大話設計模式》; 結構圖: 優點: 缺點: 應用場景: 註意事項: 示例: 1.結構類的實現: 被裝飾抽象類和被裝飾具體類 裝飾抽象類和具體裝飾類 客戶端 執行結果 2.裝飾器模式之DOTA英雄學習技能 英雄 ...
  • 作業小結3 規格化設計的發展歷史 最早的程式設計都是採用機器語言來編寫的,直接使用二進位碼來表示機器能夠識別和執行的指令和數據。簡單來說,就是直接編寫0和1的序列來代表程式語言。例如:使用0000代表載入(LOAD),0001代表存儲(STORE)等。 面向機器的語言通常情況下被認為是一種“低級語言 ...
  • 課程簡介: 這是一套目前為止我覺得最適合小白學習的體系非常完整的Python爬蟲課程,使用的Python3.6的版本,用到anaconda來開發python程式,老師講解的很細緻,課程體系設置的也非常棒,完全是從淺入深一點點講解,從Python爬蟲環境的安裝開始,講解了最最基本的urllib包如何使 ...
  • 博客地址:http://www.cnblogs.com/yudanqu/ 一、遞歸 遞歸調用:一個函數,調用的自身,稱為遞歸調用 遞歸函數:一個可以調用自身的函數稱為遞歸函數 凡是迴圈能幹的事,遞歸都能幹 下麵我們通過兩段代碼簡單看一下遞歸和非遞歸的區別: 輸入一個大於等於1的數,求1到n的和! 下 ...
  • 需要的聯繫我,QQ:1844912514 第1周 開課介紹 python發展介紹 第一個python程式 變數 字元編碼與二進位 字元編碼的區別與介紹 用戶交互程式 if else流程判斷 while 迴圈 while 迴圈優化版本 for 迴圈及作業要求 第2周 本節雞湯 模塊初識 pyc是什麼 ...
  • Java開源生鮮電商平臺-伺服器部署設計與架構(源碼可下載) 補充說明:Java開源生鮮電商平臺-伺服器部署設計與架構,指的是通過伺服器正式上線整個項目,進行正式的運營。 回顧整個章節,我們涉及到以下幾個方面: 1. 買家端 2. 賣家端。 3. 銷售端 4. 配送端。 5.系統運營端。 6.公司網 ...
  • 一、什麼是Servlet Servlet 運行在服務端的Java小程式, 是sun公司提供一套規範(介面) 主要功能: 用來處理客戶端請求 響應給瀏覽器的動態資源 servlet的實質就是java代碼, 通過java的API動態的向客戶端輸出內容 以後寫的程式就不在是在本地執行了。 而是編譯成位元組碼 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...