1. 程式人生 > >淺談使用單元素的列舉型別實現單例模式

淺談使用單元素的列舉型別實現單例模式

簡介

通常情況下,我們寫單例模式的時候無非就是三個步驟:構造器私有化,宣告私有靜態變數,提供靜態獲取例項的方法。簡單說就是以下這種方式:

class SingletonA {
    private static SingletonA instence = new SingletonA();
    private SingletonA() {
    }
    public static SingletonA getInstance() {
        return instence;
    }
}

這是最基本的單例模式的寫法,考慮到執行緒安全的問題,會用synchronized

關鍵字修飾getInstance()方法,另外還有餓漢式、懶漢式、靜態內部類、雙重校驗鎖的寫法。
但是這種寫法存在缺陷,可以利用反射的方式來例項化多個不同的例項,如下所示:

private static void testReflex() {

        try {

            SingletonA s1 = SingletonA.getInstance();
            Class<SingletonA> cls = SingletonA.class;
            Constructor<SingletonA> constructor = cls
                    .getDeclaredConstructor(new
Class[] {}); constructor.setAccessible(true); SingletonA s2 = constructor.newInstance(new Object[] {}); System.out.println(s1 == s2);//false } catch (Exception e) { e.printStackTrace(); } }

這種情況下,就會出現多個不同的例項,從而導致一些亂七八糟的結果。
還有一種情況就是在序列化和反序列換的時候也會出現多個不同的例項,如下:

class SingletonB implements Serializable {

    private static SingletonB instence = new SingletonB();

    private SingletonB() {
    }

    public static SingletonB getInstance() {
        return instence;
    }
}
private static void testSingletonB() {
        File file = new File("singleton");
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream(file));
            SingletonB SingletonB1 = SingletonB.getInstance();

            oos.writeObject(SingletonB1);
            oos.close();
            ois = new ObjectInputStream(new FileInputStream(file));
            SingletonB SingletonB2 = (SingletonB) ois.readObject();
            System.out.println(SingletonB1 == SingletonB2);//false

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

這種情況也會出現有多個例項的問題,這個問題可以在類中新增readResolve()方法來避免,即:

class SingletonB implements Serializable {

    private static SingletonB instence = new SingletonB();

    private SingletonB() {
    }

    public static SingletonB getInstance() {
        return instence;
    }

    // 不新增該方法則會出現 反序列化時出現多個例項的問題
    public Object readResolve() {
        return instence;
    }
}

這樣在反序列化的時候就不會出現多個例項。

使用單元素的列舉實現單例模式

一個最簡單的POJO類,如下:

enum SingletonC implements Serializable {
    INSTANCE;
    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

}

測試方法:

    private static void testEnum() {
        File file = new File("singletonEnum");
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {

            oos = new ObjectOutputStream(new FileOutputStream(file));
            SingletonC singleton = SingletonC.INSTANCE;
            oos.writeObject(SingletonC.INSTANCE);
            oos.close();
            ois = new ObjectInputStream(new FileInputStream(file));
            SingletonC singleton2 = (SingletonC) ois.readObject();
            System.out.println(singleton == singleton2);//true

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

這種實現單例模式的方式是在 1.5之後才出現的,

這種方法在功能上與公有域方法相近,但是它更加簡潔,無償提供了序列化機制,絕對防止多次例項化,即使是在面對複雜序列化或者反射攻擊的時候。雖然這種方法還沒有廣泛採用,但是單元素的列舉型別已經成為實現Singleton的最佳方法。 —-《Effective Java 中文版 第二版》

相關推薦

使用元素列舉型別實現模式

簡介 通常情況下,我們寫單例模式的時候無非就是三個步驟:構造器私有化,宣告私有靜態變數,提供靜態獲取例項的方法。簡單說就是以下這種方式: class SingletonA { private static SingletonA inst

使用私有構造方法或者列舉型別實現

單例(Singleton)是指只例項化一次的類。 單例表示本質上唯一的系統元件,例如檔案系統或者視窗管理器。 package com.googlecode.javatips4u.effectivejava.singleton; publicclass StaticFin

web前端開發技術之對HTML5 智能表的理解

提示 goods 表單 加載完成 空格 日期和時間 url 顯示 指向 Html5新增input的form屬性,用於指向特定form表單的id,實現input無需放在form標簽之中,即可通過表單進行提交。 <FORM id=xinzeng> … </FO

3. 【建立與銷燬物件】用同步、靜態內部類和列舉型別強化模式

本文是《Effective Java》讀書筆記第3條。 單例模式,顧名思義,就是當你需要並且僅需要某個類只有一個例項的時候所採用的設計模式。 /** * 餓漢式單例模式 */ public class Singleton { private

java中用列舉實現模式

列舉單例(Enum Singleton)是實現單例模式的一種新方式,儘管單例模式在java中已經存在很長時間了,但是列舉單例相對來說是一種比較新的概念,列舉這個特性是在Java5才出現的,這篇文章主要講解關於為什麼我們應該使用列舉來實現單例模式,它與傳統方式實現的單例模式

為什麼強烈建議大家使用列舉實現

關於單例模式,我的部落格中有很多文章介紹過。作為23種設計模式中最為常用的設計模式,單例模式並沒有想象的那麼簡單。因為在設計單例的

java中內置的觀察者模式與動態代理的實現

所有 代理 notify play ani effect 一個 indicate protected 一.關於觀察者模式 1.將觀察者與被觀察者分離開來,當被觀察者發生變化時,將通知所有觀察者,觀察者會根據這些變化做出對應的處理。 2.jdk裏已經提供對應的Observer

1.3Spring(IOC容器的實現)

tap 就是 parser pojo file abstract throw cdd moni 這一節我們來討論IOC容器到底做了什麽。 還是借用之前的那段代碼 ClassPathXmlApplicationContext app = new ClassPathXmlAp

分散式鎖--基於Zookeeper實現

淺談分散式鎖--基於Zookeeper實現篇: 1、基於zookeeper臨時有序節點可以實現的分散式鎖。其實基於ZooKeeper,就是使用它的臨時有序節點來實現的分散式鎖。 來看下Zookeeper能不能解決前面提到的問題。     鎖無法釋放:使用

分散式鎖--基於資料庫實現

淺談分散式鎖--基於資料庫實現篇 1、基於資料庫表     要實現分散式鎖,最簡單的方式可能就是直接建立一張鎖表,然後通過操作該表中的資料來實現了。     當我們要鎖住某個方法或資源時,我們就在該表中增加一條記錄,想要釋放鎖的

JavaScript模擬$(HTML字串)實現建立DOM物件

JavaScript裡動態建立標準DOM物件一般使用:document.createElement()方法。 但在實際使用過程中,可能會希望直接根據HTML字串建立DOM節點,模擬$(HTML字串)建立DOM物件的方法。 1、思路: ① 用document.createElement()

instanceof 和 typeof 的實現原理

typeof 實現原理 typeof 一般被用於判斷一個變數的型別,我們可以利用 typeof 來判斷number, string, object, boolean, function, undefined, symbol 這七種型別,這種判斷能幫助

.NET中的型別和裝箱/拆箱原理

談到裝箱拆箱,DebugLZQ相信給位園子裡的博友一定可以娓娓道來,大概的意思就是值型別和引用型別的相互轉換唄---值型別到引用型別叫裝箱,反之則叫拆箱。這當然沒有問題,可是你只知道這麼多,那麼DebugLZQ建議你花點時間看看樓主這篇文章,繼續前幾篇博文的風格--淺談雜侃

關聯規則以及Apriori演算法matlab實現

關聯規則分析也叫做購物籃分析,最早是為發現超市銷售資料庫中不同商品之間的關聯關係。例如一個超市的經理想要更多的瞭解顧客的購物習慣,比如“哪組商品可能會在一次購物中同時被購買?”或者“某顧客購買了個人電腦,那該顧客三個月後購買數碼相機的概率有多大?”他可能會發現如

md5加密 以及C++實現

md5加密是我們生活中十分常見的加密演算法。 起因:我是最近在寫一個H5 的專案時接觸到的這個演算法,這個演算法極大的引起了我的好奇心,是登陸介面,要求是將使用者輸入的密碼使用md5加密之後,再傳回伺服器,當時我十分不理解原因是什麼. 廢話少說 原因

實人認證程式實現

淺談實人認證的技術可行性和驗證必要性 之前的實名認證模式是使用者手動上傳手持身份證照片,後臺管理員來稽核。無法做到實時認證,且需要人力成本。 實人認證模式 : 身份證照片 本人自

線段樹原理及實現

pri 二叉搜索樹 進行 span 但是 build += std 葉子 大家好,給大家介紹完了樹狀數組(有興趣的讀者可以在我的博客文章中閱讀),現在來給大家介紹另一種數據結構——線段樹。它們結構都有共同點,但是線段樹更為復雜,功能也更為強大,接下來

openstack中使用linux_bridge實現vxlan網絡

strong 實現 9.png oar 一個 14. tab 組成 路由 openstack環境: 1 版本:ocata 2 系統:ubuntu16.04.2 3 控制節點 1個 + 計算節點 1個 4 控制節點網卡為ens33,ip = 172.171.5.200

架構之路:前後端分離模式

前言:分離模式   對前後端分離研究了一段時間,恰逢公司有一個大專案決定嘗試使用前後端分離模式進行,便參與其中。該專案從2016年初立項至今,平平穩穩得度過,但也湧現出越來越多的問題,絕對不是說前後端分離模式不好,而是很多公司在嘗試前後端分離的時候沒有做好充分得

轉《架構之路:前後端分離模式

原文連結:https://www.cnblogs.com/shanrengo/p/6397734.html前言:分離模式  對前後端分離研究了一段時間,恰逢公司有一個大專案決定嘗試使用前後端分離模式進行,便參與其中。該專案從2016年初立項至今,平平穩穩得度過,但也湧現出