1. 程式人生 > >單例模式(懶漢式和餓漢式區別)

單例模式(懶漢式和餓漢式區別)

單例模式

所謂單例模式,就是保證類在記憶體中只有一個物件

而如何保證類在記憶體中只有一個物件?

思考一下,我們平時在例項化類的物件時,基本都是通過new 的方式來例項化一個物件,其實說白了,就是呼叫了需要例項化類的預設的構造方法,所以為了保證類只有一個物件,我們需要將類的物件設定為private

1)控制類的建立,不讓其他類建立本類的物件,即需要設定private屬性

2)在本類中定義一個本類的物件

public class test {

	public static void main(String[] args) {
		//分別例項化物件 s1,s2
		Singleton s1 = Singleton.singleton;
		Singleton s2 = Singleton.singleton;
		System.out.println(s1 == s2);
	}

}
class Singleton{
	//私有的建構函式,保證外類不能例項化本類
	private Singleton(){}
	//自己建立一個類的例項化
	public static Singleton singleton  = new Singleton();
}

在這裡輸出一下,看一看s1和s2是不是引用的同一個例項s的地址

結果為true,可以看出,通過建立私有建構函式這樣是可以保證單例的

但是,會有一個問題!!

public class test {

	public static void main(String[] args) {
		//分別例項化物件 s1,s2
		Singleton s1 = Singleton.singleton;
		//這裡突然 將Singleton中的變成了null
		Singleton.singleton = null;
		Singleton s2 = Singleton.singleton;
		System.out.println(s1 == s2);
	}

}
class Singleton{
	//私有的建構函式,保證外類不能例項化本類
	private Singleton(){}
	//自己建立一個類的例項化
	public static Singleton singleton = new Singleton();
}

在執行main方法的時候,突然有個程序插了進來,將Singleton中的s變成了null(多執行緒中,很常見),這時就相當於引用了兩個地址,即不符合了單例的定義

所有,只用private設定建構函式是不夠的,為此,還需要進行第三步

3)提供公共的訪問方式

public class test {

	public static void main(String[] args) {
		//分別例項化物件 s1,s2
		Singleton s1 = Singleton.getInstance();
		Singleton s2 = Singleton.getInstance();
		System.out.println(s1 == s2);
	}

}
class Singleton{
	//私有的建構函式,保證外類不能例項化本類
	private Singleton(){}
	//自己建立一個類的例項化
	private static Singleton singleton = new Singleton();
	//建立一個get方法,返回一個例項s
	public static Singleton getInstance(){
		return singleton;
	}
}

先將Sinleton類中的例項化私有(private),使得外界不能呼叫,然後建立要給get方法,返回一個例項s,這樣,就保證了在外類中不能更改s的值,即保證了單例的實現

其實上述的方式 就是單例模式中的餓漢式

所謂餓漢式,就是直接創建出類的例項化;

而對於懶漢式,就是在需要的時候再建立類的例項化

class Singleton {
	// 私有的建構函式,保證外類不能例項化本類
	private Singleton() {
	}

	// 自己建立一個類的例項化
	private static Singleton singleton;

	// 建立一個get方法,返回一個例項s
	public static Singleton getInstance(){
		//判斷singleton是否為null,如果為null,即判定需要例項化
		if (singleton == null) {
			singleton = new Singleton();
		}
		return singleton;
	}
}

對於懶漢式,就是在getInstance方法中增加一個if判斷,判斷當前物件是否為null,如果為null,則建立例項

===============================分割線=============================================

這裡來進行一個比較,究竟餓漢式和懶漢式哪一個更好;

餓漢式:簡單來說就是空間換時間,因為上來就例項化一個物件,佔用了記憶體,(也不管你用還是不用)

懶漢式:簡單的來說就是時間換空間,與餓漢式正好相反

這時,會有人說,懶漢式比餓漢式更好,其實恰恰相反,這裡舉個例子!

public static Singleton getInstance(){
		//判斷singleton是否為null,如果為null,即判定需要例項化
		if (singleton == null) {
			//
			
			singleton = new Singleton();
		}
		return singleton;
	}

比如,現在有A執行緒和B執行緒,A執行緒剛好在這個getInstance()方法中,剛剛判斷完非空(此時為null),即需要建立例項,然而,就是這麼巧,B執行緒搶到了CPU的執行權,A執行緒sleep了,這時,B執行緒也進行了這個判斷,和A一樣,都需要建立例項,而這時,A也搶到了CPU,這時,B就sleep了,然後A執行了例項化後,B又搶到CPU執行權,然後B也例項化,這時,出現問題了,A和B都例項化了一個物件,這就是赤果果的兩個物件呀,單例呢,唯一呢,全都沒了。

而且,上面說過,懶漢式和餓漢式的區別具體就是時間和空間的轉換,現在開發的時候關心的應該還是時間,對於空間,完全可以通過硬體來優化呀,加大記憶體!!!但是減少時間計算就很麻煩了,額!!

所以說,對於懶漢式在多執行緒中式不支援的,所以相對來說,更多的是用餓漢式

===============================分割線=============================================

之前百度單例的時候,發現還有第三種方法,簡單試了一下,也能達到單例的效果,但是沒有具體嘗試,這裡粘個程式碼和執行結果看看就好了

public class test {

	public static void main(String[] args) {
		// 分別例項化物件 s1,s2
		Singleton s1 = Singleton.singleton;
		// 在這裡 更改singleton的值是不允許的,因為設定了final屬性
		// Singleton.singleton = null;
		Singleton s2 = Singleton.singleton;
		System.out.println(s1 == s2);
	}
}

class Singleton {
	// 私有的建構函式,保證外類不能例項化本類
	private Singleton() {
	}

	// 自己建立一個類的例項化
	public static final Singleton singleton = new Singleton();

}

其實這種方法就是將singleton類中例項化方法加了一個final屬性,就是不允許更改其值,所以在外類進行對其進行修改時,是不會允許的,同樣達到了單例的效果。