設計模式–單例模式(一)懶漢式和餓漢式 – JAVA編程語言程序開發技術文章

文是設計模式學習筆記的第二篇文章,主要分析的是單例模式。包括懶漢式,餓漢式,登記式,以及懶漢式的改進型,
還有一個關於讀取propertoes配置文件的實例。預計分為三節。這是第一節,先分析最簡單的懶漢式和餓漢式。

單例模式是設計模式中比較簡單的一種。適合於一個類隻有一個實例的情況,比如窗口管理器,打印緩沖池和文件系統,
它們都是原型的例子。典型的情況是,那些對象的類型被遍及一個軟件系統的不同對象訪問,因此需要一個全局的訪問
指針,這便是眾所周知的單例模式的應用。當然這隻有在你確信你不再需要任何多於一個的實例的情況下。
單例模式的用意在於前一段中所關心的。通過單例模式你可以:

                     一、確保一個類隻有一個實例被建立
                     二、提供瞭一個對對象的全局訪問指針
                     三、在不影響單例類的客戶端的情況下允許將來有多個實例

經典的單例模式有三種,懶漢式、餓漢式和 登記式。

懶漢式的特點是延遲加載,比如配置文件,采用懶漢式的方法,顧名思義,懶漢麼,很懶的,配置文件的實例直到用到的
時候才會加載。。。。。。
餓漢式的特點是一開始就加載瞭,如果說懶漢式是“時間換空間”,那麼餓漢式就是“空間換時間”,因為一開始就創建瞭實例,所以每次用到的之後直接返回就好瞭。

 

讓我們先看下代碼:

 

懶漢式:

[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; 
    } 

看下客戶端的測試代碼:
[java] 
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為同一實例(懶漢式)<<<<<

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

 

餓漢式:

[java] 
//餓漢式單例模式 
public class MySingleton2 { 
 
    //設立靜態變量,直接創建實例 
    private static MySingleton2 mySingleton = new MySingleton2(); 
 
    private MySingleton2(){ 
        //私有化構造函數 
        System.out.println("–>餓漢式單例模式開始調用構造函數"); 
    } 
     
    //開放一個公有方法,判斷是否已經存在實例,有返回,沒有新建一個在返回 
    public static MySingleton2 getInstance(){ 
        System.out.println("–>餓漢式單例模式開始調用公有方法返回實例"); 
        return mySingleton; 
    } 

看下客戶端的測試代碼:
[java] 
/**
 * 餓漢式單例模式
 * 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(); 

[java] 
/**
 * @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進行控制,那麼就會有兩個實例被創建。

發佈留言