【設計模式】 單例模式:別忘了還可以用枚舉

概念

單例模式就是爲了確保一個類當中只有一個實例,並且提供一個全局的公有訪問點。

通俗的說,當我們需要用到某個對象的實例的時候,我們無需進行其它多餘操作,而是直接通過某個接口獲取到它的實例,並且這個實例在整個系統中保證唯一。

要求

這就需要我們滿足兩點要求:

  1. 生成的類的實例是唯一的,也就是說生成對象實例的代碼只能夠運行一次
  2. 生成實例的方法必須是靜態方法。因爲非靜態方法的必須通過實例進行調用,如果已經有了實例,我們還需要生成實例的方法幹什麼呢,就成了先有雞還是先有蛋的問題了。

實現方式

  • 餓漢式
  • 懶漢式
  • 雙重檢查
  • 靜態內部類
  • 枚舉

餓漢式

不管需不需要,我都要創建一個實例。

1. 使用靜態常量實現

1) 構造器私有化(防止new)

2) 在類的內部創建對象

3) 向外暴露一個靜態的公共方法:getInstance

//餓漢式(靜態變量)
class Singleton{

 	//1. 構造器私有化, 防止外部能new
 	private Singleton() {}

    //2.本類內部創建對象實例
    private static Singleton instance = new Singleton();

    //3. 提供一個公有的靜態方法,返回實例對象
    public static Singleton getInstance() {
 	    return instance;
 	}
}

優點:這種寫法比較簡單,在類裝載的時候就完成實例化。避免了線程同步問題。

缺點:在類裝載的時候就完成實例化,沒有達到Lazy Loading的效果。如果從始至終從未使用過這個實例,則會造成內存的浪費。

2. 使用靜態代碼塊實現

1) 構造器私有化(防止new)

2) 在類的內部使用靜態代碼塊創建對象

3) 向外暴露一個靜態的公共方法:getInstance

//餓漢式(靜態代碼塊)
class Singleton{

 	//1. 構造器私有化, 防止外部能new
 	private Singleton() {}

    //2.本類內部創建對象實例
    private static Singleton instance;
    static { // 在靜態代碼塊中,創建單例對象
 		instance = new Singleton();
    }

    //3. 提供一個公有的靜態方法,返回實例對象
    public static Singleton getInstance() {
 	    return instance;
    }
}

優缺點:這種方式和上面的方式其實類似,只不過將類實例化的過程放在了靜態代碼塊中,也是在類裝載的時候,就執行靜態代碼塊中的代碼,初始化類的實例。優缺點和上面是一樣的。

懶漢式

用的時候才創建,所以叫懶漢式。

1) 構造器私有化(防止new)

2) 在類的內部使用靜態代碼塊創建對象

3) 向外暴露一個靜態的公共方法:getInstance

//懶漢式
class Singleton {
    //1.構造方法私有化,防止外部調用
    private Singleton() {}

    //2.聲明該靜態對象
    private static Singleton instance;

    //3.提供一個靜態的公有方法,當使用到該方法時,纔去創建instance
    public static Singleton synchronized getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

優點:起到了Lazy Loading 的效果。

缺點:效率太低,每個線程在想獲得類的實例時候,執行getInstance()方法都要進行同步。而其實這個方法只執行一次實例化代碼就夠了,後面的想獲得該類實例,直接return 就行了。方法進行同步效率太低。

結論:在實際開發中,不推薦使用這種方式.

雙重檢查(較推薦)

//雙重檢查
class Singleton {
    //1. 私有化構造方法
    private Singleton() {}

    //2. 聲明靜態對象
 	private static Singleton instance;
 	

 	//提供一個靜態的公有方法,加入雙重檢查代碼,同時解決線程安全,懶加載和效率問題
    public static synchronized Singleton getInstance() {
     	if(instance == null) {
            synchronized (Singleton.class) {
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
    }
    return instance;
}

1) Double-Check 概念是多線程開發中常使用到的,如代碼中所示,我們進行了兩次if (singleton == null)檢查,這樣就可以保證線程安全了。

2) 這樣,實例化代碼只用執行一次,後面再次訪問時,判斷if (singleton == null),直接return 實例化對象,也避免的反覆進行方法同步.

3) 線程安全;延遲加載;效率較高

4) 結論:在實際開發中,較爲推薦使用這種單例設計模式

靜態內部類模式(推薦)

//靜態內部類(推薦)
public class Singleton {
    //1.將構造方法私有化
    private Singleton(){}

    //寫一個靜態內部類,該類中有一個私有的靜態實例
    private static class SingletonHolder{
        //靜態初始化器,由JVM保證線程安全
        private static Singleton ourInstance = new Singleton();
    }

    //留給外界初始化的方法,返回該對象
    public static Singleton getInstance(){
        return SingletonHolder.ourInstance;
    }
}

1) 這種方式採用了類裝載的機制來保證初始化實例時只有一個線程。

2) 靜態內部類在該類需要實例化,調用getInstance 方法時,纔會裝載SingletonInstance類,從而完成Singleton的實例化。

3) 類的靜態屬性只會在第一次加載類的時候初始化,所以在這裏,JVM 幫助我們保證了線程的安全性,在類進行初始化時,別的線程是無法進入的。

4) 優點:這種方法基於在懶漢模式中提出的,JVM在類的初始化階段給出的線程安全性保證,將singleton 的實例化操作放置到一個靜態內部類中,在第一次調用getInstance() 方法時,JVM纔會去加載InstanceHolder 類,同時初始化singleton 實例,因此,即使我們不採取任何同步策略,getInstance() 方法也是線程安全的。

5) 結論:線程安全,效率高,推薦使用。

枚舉(推薦)

這種方式是Effective Java 作者Josh Bloch 提倡的方式,他說最佳的單例實現模式就是枚舉模式。利用枚舉的特性,讓JVM來幫我們保證線程安全和單一實例的問題。除此之外,寫法還特別簡單。

//使用枚舉實現單例, 推薦
public enum Singleton {
	INSTANCE;
	public void sayOK() {
		System.out.println("ok~");
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章