1. 程式人生 > >[轉]設計模式--單例模式(一)懶漢式和餓漢式

[轉]設計模式--單例模式(一)懶漢式和餓漢式

打印 是否 調用構造 餓漢 一段 tools 會有 輸出結果 java

單例模式是設計模式中比較簡單的一種。適合於一個類只有一個實例的情況,比如窗口管理器,打印緩沖池和文件系統, 它們都是原型的例子。典型的情況是,那些對象的類型被遍及一個軟件系統的不同對象訪問,因此需要一個全局的訪問 指針,這便是眾所周知的單例模式的應用。當然這只有在你確信你不再需要任何多於一個的實例的情況下。 單例模式的用意在於前一段中所關心的。通過單例模式你可以: 一、確保一個類只有一個實例被建立 二、提供了一個對對象的全局訪問指針 三、在不影響單例類的客戶端的情況下允許將來有多個實例 經典的單例模式有三種,懶漢式、餓漢式和 登記式。 懶漢式的特點是延遲加載,比如配置文件,采用懶漢式的方法,顧名思義,懶漢麽,很懶的,配置文件的實例直到用到的 時候才會加載。。。。。。

餓漢式的特點是一開始就加載了,如果說懶漢式是“時間換空間”,那麽餓漢式就是“空間換時間”,因為一開始就創建了實例,所以每次用到的之後直接返回就好了。

讓我們先看下代碼:

懶漢式:

//懶漢式單例模式

技術分享
//懶漢式單例模式
public class MySingleton {

	//設立靜態變量
	private static MySingleton mySingleton = null;

	private MySingleton(){
		//私有化構造函數
		System.out.println("-->懶漢式單例模式開始調用構造函數");
	}
	
	//開放一個公有方法,判斷是否已經存在實例,有返回,沒有新建一個在返回
	public static MySingleton getInstance(){
		System.out.println("-->懶漢式單例模式開始調用公有方法返回實例");
		if(mySingleton == null){
			System.out.println("-->懶漢式構造函數的實例當前並沒有被創建");
			mySingleton = new MySingleton();
		}else{
			System.out.println("-->懶漢式構造函數的實例已經被創建");
		}
		System.out.println("-->方法調用結束,返回單例");
		return mySingleton;
	}
}
看下客戶端的測試代碼: 技術分享
public class Client {
	
	/**
	 * 懶漢式單例模式
	 * MySingleton
	 */
	public static void myprint(){
		System.out.println("-----------------懶漢式單例模式----------------");
		System.out.println("第一次取得實例(懶漢式)");
		MySingleton s1 = MySingleton.getInstance();
		System.out.println("第二次取得實例(懶漢式)");
		MySingleton s2 = MySingleton.getInstance();
		if(s1==s2){
			System.out.println(">>>>>s1,s2為同一實例(懶漢式)<<<<<");
		}
		System.out.println();
	}
        /**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//懶漢式
		myprint();
		//餓漢式
		//myprint2();
		//懶漢式改進
		//myprint2a();
		//登記式
		//myprint3();

	}

}
輸出結果為:

-----------------懶漢式單例模式---------------- 第一次取得實例(懶漢式),懶漢式單例模式開始調用公有方法返回實例, 懶漢式構造函數的實例當前並沒有被創建懶漢式單例模式開始調用構造函數 ,方法調用結束,返回單例 第二次取得實例(懶漢式) ,懶漢式單例模式開始調用公有方法返回實例 ,懶漢式構造函數的實例已經被創建 ,方法調用結束,返回單例 s1,s2為同一實例(懶漢式)

可以看出,在第一次調用公有方法的時候,並沒有實例,所以我們創建了一個實例,之後再訪問的時候,因為已經有一個已經創建好的實例,所以直接返回了。

餓漢式:

  1. //餓漢式單例模式
  2. public class MySingleton2 {
  3. //設立靜態變量,直接創建實例
  4. private static MySingleton2 mySingleton = new MySingleton2();
  5. private MySingleton2(){
  6. //私有化構造函數
  7. System.out.println("-->餓漢式單例模式開始調用構造函數");
  8. }
  9. //開放一個公有方法,判斷是否已經存在實例,有返回,沒有新建一個在返回
  10. public static MySingleton2 getInstance(){
  11. System.out.println("-->餓漢式單例模式開始調用公有方法返回實例");
  12. return mySingleton;
  13. }
  14. }
技術分享
//餓漢式單例模式
public class MySingleton2 {

	//設立靜態變量,直接創建實例
	private static MySingleton2 mySingleton = new MySingleton2();

	private MySingleton2(){
		//私有化構造函數
		System.out.println("-->餓漢式單例模式開始調用構造函數");
	}
	
	//開放一個公有方法,判斷是否已經存在實例,有返回,沒有新建一個在返回
	public static MySingleton2 getInstance(){
		System.out.println("-->餓漢式單例模式開始調用公有方法返回實例");
		return mySingleton;
	}
}
看下客戶端的測試代碼: 技術分享
	/**
	 * 餓漢式單例模式
	 * MySingleton2
	 */
	public static void myprint2(){
		System.out.println("-----------------餓漢式單例模式----------------");
		System.out.println("第一次取得實例(餓漢式)");
		MySingleton2 s1 = MySingleton2.getInstance();
		System.out.println("第二次取得實例(餓漢式)");
		MySingleton2 s2 = MySingleton2.getInstance();
		if(s1==s2){
			System.out.println(">>>>>s1,s2為同一實例(餓漢式)<<<<<");
		}
		System.out.println();
	}
技術分享
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//懶漢式
		//myprint();
		//餓漢式
		myprint2();
		//懶漢式改進
		//myprint2a();
		//登記式
		//myprint3();

	}

輸出結果為:

-----------------餓漢式單例模式---------------- 第一次取得實例(餓漢式) -->餓漢式單例模式開始調用構造函數 -->餓漢式單例模式開始調用公有方法返回實例 第二次取得實例(餓漢式) -->餓漢式單例模式開始調用公有方法返回實例 >>>>>s1,s2為同一實例(餓漢式)<<<<<

總結一下,兩種方案的構造函數和公用方法都是靜態的(static),實例和公用方法又都是私有的(private)。但是餓漢式每次調用的時候不用做創建,直接返回已經創建好的實例。這樣雖然節省了時間,但是卻占用了空間,實例本身為static的,會一直在內存中帶著。懶漢式則是判斷,在用的時候才加載,會影響程序的速度。最關鍵的是,在並發的情況下,懶漢式是不安全的。如果兩個線程,我們稱它們為線程1和線程2,在同一時間調用getInstance()方法,如果線程1先進入if塊,然後線程2進行控制,那麽就會有兩個實例被創建。

[轉]設計模式--單例模式(一)懶漢式和餓漢式