枚舉實現單例模式

寫在前面

單例模式定義: 一個類 只能創建一個實例。

使用一個對象來做就不用實例化多個對象!這就能減少我們空間和內存的開銷~

我們使用靜態類.doSomething()和使用單例對象調用方法的效果是一樣的啊。

沒錯,效果就是一樣的。使用靜態類.doSomething()體現的是基於對象,而使用單例設計模式體現的是面向對象。

編寫單例模式的代碼其實很簡單,就分了三步:

將構造函數私有化

在類的內部創建實例

提供獲取唯一實例的方法

常用的單例模式

  1. 餓漢式
public class Liuge36 {
   
    // 1.構造函數私有化
    private Liuge36(){

    }
    // 2.在類的內部創建實例
    private static Liuge36 liuge36 = new Liuge36();

    // 3.提供獲取實例的唯一方法
    public static Liuge36 getInstance(){
        return liuge36;
    }

}

一上來就創建對象了,如果該實例從始至終都沒被使用過,則會造成內存浪費。

  1. 簡單懶漢式 (在方法上加鎖)
    既然說一上來就創建對象,如果沒有用過會造成內存浪費:
    那麼我們就設計用到的時候再創建對象!
public class Liuge36 {
    // 1.將構造函數私有化
    private Liuge36(){

    }
    // 2.先不創建對象,等用到的時候 ,再開始創建
    private static Liuge36 liuge36 = null;
    
    /**
     * 3.調用獲取實例的方法,說明要用到實例對象了
     * synchronized 加鎖之後才能保證在多線程下代碼可用,不加鎖僅僅適用於單線程的情況
     */
    public static synchronized Liuge36 getInstance(){

        // 先判斷對象是否爲空,如果爲空,先創建再返回出去
        if (liuge36 == null) {
            liuge36 = new Liuge36();
        }
        return liuge36;

    }
}
  1. DCL雙重檢測加鎖(進階懶漢式)
    上面那種直接在方法上加鎖的方式其實不夠好,因爲在方法上加了內置鎖在多線程環境下性能會比較低下,所以我們可以將鎖的範圍縮小。
public class Liuge36 {
    // 1.將構造函數私有化
    private Liuge36(){

    }

    /**
     * 利用靜態變量liuge36來記錄Liuge36的唯一實例
     *
     * volatile 關鍵字確保:當變量liuge36 被初始化成 Liuge36實例時,
     * 多個線程正確地處理liuge36變量
     *
     * volatile有內存屏障的功能!
     */
    private static volatile Liuge36 liuge36 = null;

    // 提供獲取唯一實例的方法
    public static Liuge36 getInstance(){
        if (liuge36 == null){

            // 同步代碼塊 ,將鎖的範圍縮小,提高性能
            synchronized (Liuge36.class){
                // 再次判斷對象是否創建過
                if (liuge36 == null){
                    liuge36 = new Liuge36();
                }
            }

        }
        return liuge36;
    }
}

  1. 靜態內部類實現懶漢式
public class Liuge36 {
    // 1.將構造函數私有化
    private Liuge36(){

    }

    /**
     * 使用靜態內部類的方式實現懶加載
     * 初始化靜態數據時,Java提供了的線程安全性保證。(所以不需要任何的同步)
     */
    private static class LazyHolder{
        // 創建單例對象
        private static final Liuge36 instance = new Liuge36();
    }
    // 提供獲取實例的唯一方法
    public static Liuge36 getInstance(){
        return LazyHolder.instance;
    }

}
  1. 枚舉方式
    Joshua Bloch說“單元素的枚舉類型已經成爲實現Singleton的最佳方法
public class Liuge36 {
    // 1.將構造函數私有化
    private Liuge36(){

    }
    // 2 定義一個靜態枚舉類
    static enum SingletonEnum{
        INSTANCE;
        private Liuge36 liuge36;
        // 私有化枚舉的構造函數

        private SingletonEnum(){
            liuge36 = new Liuge36();
        }

        public Liuge36 getLiuge36(){
            return liuge36;
        }
    }

    // 3. 提供獲取實例的唯一方法
    public static Liuge36 getInstance(){
        return SingletonEnum.INSTANCE.getLiuge36();
    }

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

}

總的來說單例模式寫法有5種:

餓漢式

簡單懶漢式(在方法加鎖)

DCL雙重檢測加鎖(進階懶漢式)

靜態內部類實現懶漢式(最推薦寫法)

枚舉方式(最安全、簡潔寫法)

參考:java3y & https://www.jianshu.com/p/d35f244f3770

發佈了161 篇原創文章 · 獲贊 31 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章