為什麼要用枚舉實現Singleton--java

来源:http://www.cnblogs.com/AprilCal/archive/2016/04/23/5426007.html
-Advertisement-
Play Games

為什麼要用枚舉實現Singleton java 理由一:無需再考慮可序列化的情況   《effective java》第77條:對於實例控制,枚舉類型優先於readResolve   說到readResolve,有的人可能會不甚清楚其作用,簡單來說,readR ...


為什麼要用枚舉實現Singleton--java

  • 理由一:無需再考慮可序列化的情況

      《effective java》第77條:對於實例控制,枚舉類型優先於readResolve

      說到readResolve,有的人可能會不甚清楚其作用,簡單來說,readResolve的作用是這樣的:readResolve特性允許你用readObject創建的實例代替另一個實例。
      對於一個正在被反序列化的對象,如果他的類定義了一個readResolve方法,並具備正確的聲明,那麼在反序列化之後,新建對象上的readResolve方法就會被調用。然後,該方法返回的對象引用將會被返回,取代新建的對象。

      《effective java》中 第三條提到,如果在這個類的聲明中加上了‘inplments Serializable’的字樣,它就不再是一個Singleton。無論該類使用了預設的序列化形式,還是自定義的序列化形式,都沒有關係;也跟它是否提供了顯示的readObject方法無關。任何一個readObject方法,不管是顯示的還是預設的,它都會返回一個新建的實例。這個新建的實例不同於該類初始化時創建的實例。這也是為什麼要提供readResolve方法的原因。

    《effective java》第3條中:
      To make a singleton class that is implemented using either of the previous approaches serializable (Chapter 11), it is not sufficient merely to add implements Serializable to its declaration. To maintain the singleton guarantee, you have to declare all instance fields transient and provide a readResolve method (Item 77). Otherwise, each time a serialized instance is deserialized, a new instance will be created, leading, in the case of our example, to spurious Elvis sightings. To prevent this, add this readResolve method to the Elvis class:
    // readResolve method to preserve singleton property
    private Object readResolve() {
    // Return the one true Elvis and let the garbage collector
    // take care of the Elvis impersonator.
    return INSTANCE;
    }
      由於中文版中概念稍有混淆,特意去查閱英文原文。大體上的意思如下:
      為了使之前的方法實現的Singleton類可序列化,僅僅在聲明中加上"implements Serializable"是不夠的。為了保持Singleton,你需要聲明所有實例欄位為transient並提供readResolve方法。否則,每次一個序列化的實例被反序列化時,都會創建一個新的實例。比如在我們的例子中,會導致假冒的Elvis情況。為防止此種情況,要在Elvis類中加入下麵這個readResolve方法:
    看到這裡應該明白,對於一個需要序列化的Singleton來說,我們需要手動為其添加readResolve方法,在某些情況下,這樣做會尤為複雜。
      而用枚舉來實現Singleton則完全不必考慮,因為jvm可以保證這一點。

  • 理由二:無需再考慮通過反射調用私有構造函數的情況

      《effective java》第三條中:享有特權的客戶端可以藉助AccessiableObject.setAccessible方法,通過反射機制調用私有構造器。如果需要抵禦這種攻擊,可以修改構造器,讓他在被要求第二次創建第二個實例的時候拋出異常。

       先看一個例子



    package singleton;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    class Singleton//最普通的一種Singleton實現
    {
    private static Singleton INSTANCE=new Singleton();
    private Singleton()
    {
    System.out.println("私有構造函數被調用");
    }
    public static Singleton getInstance()
    {
    return INSTANCE;
    }
    }
    enum EnumSingleton2//枚舉實現的Singleton
    {
    INSTANCE;
    private EnumSingleton2()
    {
    System.out.println("enum 私有構造函數被調用");
    }
    }
    public class SingletonTest
    {
    public static void main(String[] args)
    throws ClassNotFoundException, IllegalAccessException,
    IllegalArgumentException, InvocationTargetException,
    NoSuchMethodException, SecurityException, InstantiationException
    {
    Class <?> cls1 = Class.forName("singleton.Singleton");
    Class <?> cls2 = Class.forName("singleton.EnumSingleton2");
    //通過反射調用Singleton的構造函數
    Constructor <?> c0=cls1.getDeclaredConstructor();
    c0.setAccessible(true);
    Singleton s=(Singleton)c0.newInstance();
    //通過反射調用EnumSingleton的構造函數
    Constructor &lt?> c1=cls2.getDeclaredConstructor();
    c1.setAccessible(true);
    EnumSingleton es=(EnumSingleton)c1.newInstance();
    }
    }


    這段代碼的執行結果如下:

    私有構造函數被調用
    enum 私有構造函數被調用
    私有構造函數被調用
    Exception in thread "main" java.lang.NoSuchMethodException: singleton.EnumSingleton2.()
    at java.lang.Class.getConstructor0(Unknown Source)
    at java.lang.Class.getDeclaredConstructor(Unknown Source)
    at singleton.SingletonTest.main(SingletonTest.java:46)

    在普通的Singleton中,我們通過反射機制調用了其私有的構造函數,而在通過反射調用enmuSingleton的私有構造函數時,則直接拋出了異常。可見,在使用枚舉實現Singleton時,jvm會替我們完成防止通過反射調用私有構造函數的工作。

  • 理由三:枚舉實例創建是線程安全的,無需再考慮Double checked locking

    我們都知道,在延遲初始化的情況下,為了保證線程安全,通常在實現Singleton的時候使用Double checked locking,而在枚舉的情況下,我們則可以完全不必考慮這些。
    關於Double checked locking實現Singleton詳見http://www.cnblogs.com/techyc/p/3529983.html
    並且此連接中有處細節需要糾正一下,用到Double checked locking時,必須要用volatile修飾單例的實例,否則將毫無意義。
    至於為什麼要用volatile修飾,我不說了,嘻嘻。

  • 總結

    總結起來使用枚舉類型實現Singleton主要有以下三大優勢:

    1:無需再考慮可序列化的情況

    2:無需再考慮通過反射調用私有構造函數的情況

    3:枚舉實例創建是線程安全的

    最後,千言萬語一個字,使用枚舉實現單例情況會好的多,但不排除某些情況用特殊的方法實現單例也同樣很高效。

參考資料http://www.javalobby.org/java/forums/t17491.html (可能需FQ


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

-Advertisement-
Play Games
更多相關文章
  • 1.這裡僅對web控制項而言,onclick事件執行的是客戶端中的代碼, 可以把事件寫在html頁面上,也可以放在調用的js文件中(此處為A.js)。 A.js: 運行結果: 2.onserverclick事件,這個是執行服務端的方法。 對應的在後臺補充相應的事件: 執行結果: 註意,當onclick ...
  • EF框架對資料庫的連接提供了一系列的預設行為,通常情況下不需要我們太多的關註。但是,這種封裝,降低了靈活性,有時我們需要對資料庫連接加以控制。 EF提供了兩種方案控制資料庫連接: 傳遞到Context的連接; Database.Connnection.Open(); 下麵詳解。 傳遞到Context ...
  • 中間因為比較忙,空了那麼多天,都感覺有點罪過了。話不多說,這一篇主要是要講C#2.0提出的一個新特性,那就是泛型。(現在都C#6.0了。囧囧) 1、什麼是泛型? C#1.0中的委托特性使方法可作為其他方法的參數來傳遞,而C#2.0中提出的泛型特性則使類型可以被參數化,從而不必再為不同的類型提供特殊版 ...
  • 相信博客園的讀者大多都是千萬“碼農”中的一員,每個人都寫過很多代碼,但並不是每一個人都能寫出高質量的代碼。rome is not built in one day !——完成高質量的代碼也不是一蹴而就的。為了寫出高質量的代碼,我們需要藉助一些手段,“代碼重構”基本上是最常用的手段,甚至是唯一的手段。... ...
  • IPerf是一個開源的測試網路寬頻並能統計並報告延遲抖動、數據包丟失率信息的控制台命令程式,通過參數選項可以方便地看出,通過設置不同的選項值對網路帶寬的影響,對於學習網路編程還是有一定的借鑒意義,至少可以玩上一段時間。 IPerf開始出現的時候是在03年,版本是1.7.0,在網上找到的僅有的系列源碼 ...
  • 運行時常量池是方法區的一部分,方法區用於存放Class的相關信息,如類名、訪問修飾符、常量池、欄位描述、方法描述等。 String.intern()是一個native方法,它的作用是:如果字元串常量池中已經包含了一個等於此String對象的字元串,則返回代表池中這個字元串的String對象;否則,將 ...
  • 本文是針對jquery 實現抽獎轉盤作者的一個補充(主要用java去實現轉盤結果生成及存儲,解決jquery 做法 非法用戶採用模擬器實現改變轉盤值的風險性),針對jQuery的具體實現,請看案例:http://www.cnblogs.com/mofish/archive/2013/01/24/28 ...
  • 不知什麼時候,也許是XE8,也許是XE8之前 .Delphi裡面多了個System.Threading的並行庫. 雖然己經有非常棒的第三方並行庫QWorker,但我還是更喜歡官方的東西. 下麵是一段使用System.Threading中ITask的代碼 這個東東裡面己經使用了一個預設的線程池,並用全 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...