1. 程式人生 > 其它 >常用設計模式之單例模式

常用設計模式之單例模式

單例模式

作者的嘮叨:

​ 我不知道單例模式是不是最常用的設計模式,但我知道單例模式是最簡單的設計模式。今天就先拿最簡單的開刀,找找自信。(有什麼不對的地方還請大家指正)

借鑑自C語言中文網:http://c.biancheng.net/view/1338.html

1.概要描述

某個類只能生成一個例項,該類提供了一個全域性訪問點供外部獲取該例項

2.特點

單例模式有 3 個特點:

  1. 單例類只有一個例項物件;
  2. 該單例物件必須由單例類自行建立;
  3. 單例類對外提供一個訪問該單例的全域性訪問點。

3.優點

單例模式的優點:

  • 單例模式可以保證記憶體裡只有一個例項,減少了記憶體的開銷。
  • 可以避免對資源的多重佔用。
  • 單例模式設定全域性訪問點,可以優化和共享資源的訪問。

4.缺點

單例模式的缺點:

  • 單例模式一般沒有介面,擴充套件困難。如果要擴充套件,則除了修改原來的程式碼,沒有第二種途徑,違背開閉原則。
  • 在併發測試中,單例模式不利於程式碼除錯。在除錯過程中,如果單例中的程式碼沒有執行完,也不能模擬生成一個新的物件。
  • 單例模式的功能程式碼通常寫在一個類中,如果功能設計不合理,則很容易違背單一職責原則。

5.應用場景

對於 Java 來說,單例模式可以保證在一個 JVM 中只存在單一例項。單例模式的應用場景主要有以下幾個方面。

  • 需要頻繁建立的一些類,使用單例可以降低系統的記憶體壓力,減少 GC。
  • 某類只要求生成一個物件的時候,如一個班中的班長、每個人的身份證號等。
  • 某些類建立例項時佔用資源較多,或例項化耗時較長,且經常使用。
  • 某類需要頻繁例項化,而建立的物件又頻繁被銷燬的時候,如多執行緒的執行緒池、網路連線池等。
  • 頻繁訪問資料庫或檔案的物件。
  • 對於一些控制硬體級別的操作,或者從系統上來講應當是單一控制邏輯的操作,如果有多個例項,則系統會完全亂套。
  • 當物件需要被共享的場合。由於單例模式只允許建立一個物件,共享該物件可以節省記憶體,並加快物件訪問速度。如 Web 中的配置物件、資料庫的連線池等。

6.程式碼實現

單例模式通常有兩種實現方式:懶漢式和餓漢式

  1. 懶漢式:該模式的特點是類載入時沒有生成單例,只有當第一次呼叫 getlnstance 方法時才去建立這個單例
  2. 餓漢式:該模式的特點是類一旦載入就建立一個單例,保證在呼叫 getInstance 方法之前單例已經存在了
public class Main {
    public static void main(String[] args) {
        LazySingleton S1 = LazySingleton.getInstance();
        S1.setName("物件1");
        System.out.println(S1.getName());
        LazySingleton S2 = LazySingleton.getInstance();
        System.out.println(S2.getName());
    }
}

    //單例模式--懶漢式實現
    class LazySingleton{
        //保證instance在所有執行緒中同步
        //類載入時沒有生成單例
        private static volatile Singleton instance = null;

        private String name;

        //私有化建構函式,避免在類在外部被例項化,對應特點2
        private LazySingleton(){}

        //對應特點3,單例類對外提供一個訪問該單例的全域性訪問點
        public static synchronized Singleton getInstance(){
            //當第一次呼叫 getlnstance 方法時才去建立這個單例
            if(instance==null){
                instance=new Singleton();
            }
            return instance;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

//餓漢式實現
public class HungrySingleton {
    //類載入時生成單例
    private static final HungrySingleton instance = new HungrySingleton();
    
    private String name;
    
    //私有化建構函式,避免在類在外部被例項化,對應特點2
    private HungrySingleton() {
    }
    
    //對應特點3,單例類對外提供一個訪問該單例的全域性訪問點
    public static HungrySingleton getInstance() {
        return instance;
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
輸出結果:

物件1

物件1

分析:兩個名稱相同說明是同一個例項物件,否則物件S2的名稱並沒有定義應該為空