常用設計模式之單例模式
阿新 • • 發佈:2022-05-30
單例模式
作者的嘮叨:
我不知道單例模式是不是最常用的設計模式,但我知道單例模式是最簡單的設計模式。今天就先拿最簡單的開刀,找找自信。(有什麼不對的地方還請大家指正)
借鑑自C語言中文網:http://c.biancheng.net/view/1338.html
1.概要描述
某個類只能生成一個例項,該類提供了一個全域性訪問點供外部獲取該例項
2.特點
單例模式有 3 個特點:
- 單例類只有一個例項物件;
- 該單例物件必須由單例類自行建立;
- 單例類對外提供一個訪問該單例的全域性訪問點。
3.優點
單例模式的優點:
- 單例模式可以保證記憶體裡只有一個例項,減少了記憶體的開銷。
- 可以避免對資源的多重佔用。
- 單例模式設定全域性訪問點,可以優化和共享資源的訪問。
4.缺點
單例模式的缺點:
- 單例模式一般沒有介面,擴充套件困難。如果要擴充套件,則除了修改原來的程式碼,沒有第二種途徑,違背開閉原則。
- 在併發測試中,單例模式不利於程式碼除錯。在除錯過程中,如果單例中的程式碼沒有執行完,也不能模擬生成一個新的物件。
- 單例模式的功能程式碼通常寫在一個類中,如果功能設計不合理,則很容易違背單一職責原則。
5.應用場景
對於 Java 來說,單例模式可以保證在一個 JVM 中只存在單一例項。單例模式的應用場景主要有以下幾個方面。
- 需要頻繁建立的一些類,使用單例可以降低系統的記憶體壓力,減少 GC。
- 某類只要求生成一個物件的時候,如一個班中的班長、每個人的身份證號等。
- 某些類建立例項時佔用資源較多,或例項化耗時較長,且經常使用。
- 某類需要頻繁例項化,而建立的物件又頻繁被銷燬的時候,如多執行緒的執行緒池、網路連線池等。
- 頻繁訪問資料庫或檔案的物件。
- 對於一些控制硬體級別的操作,或者從系統上來講應當是單一控制邏輯的操作,如果有多個例項,則系統會完全亂套。
- 當物件需要被共享的場合。由於單例模式只允許建立一個物件,共享該物件可以節省記憶體,並加快物件訪問速度。如 Web 中的配置物件、資料庫的連線池等。
6.程式碼實現
單例模式通常有兩種實現方式:懶漢式和餓漢式
- 懶漢式:該模式的特點是類載入時沒有生成單例,只有當第一次呼叫 getlnstance 方法時才去建立這個單例
- 餓漢式:該模式的特點是類一旦載入就建立一個單例,保證在呼叫 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的名稱並沒有定義應該為空