文章目錄
- 前言
- 一、舉例說明單例模式
- 1、程式代碼
- ①、皇帝類
- ②、臣子類
- ③、運行結果
- 二、單例模式的定義
- 1、單例模式通用類圖
- 2、單例模式通用代碼
- 三、單例模式的應用
- 1、單例模式的優點
- 2、單例模式的缺點
- 3、單例模式的使用場景
- 4、單例模式的注意事項
- ①、執行緒不安全的懶漢式單例模式
- ②、執行緒安全的懶漢式單例模式
- ③、餓漢式單例模式
- ④、懶漢式單例模式和餓漢式單例模式比較
- 總結
前言
開始學習Java設計模式時第一個學習的模式是單例模式,參考書籍為《設計模式之禪》第2版,在此做一個記錄以及學習心得,便于以后進行回顧
提示:以下是本篇文章正文內容,下面案例可供參考
一、舉例說明單例模式
在古代大家談論的時候只要提及皇帝,每個人都知道指的是誰,不需要在皇帝前面加上特定的稱呼,這個程序反映到設計領域就是要求一個類只能生成一個皇帝,所有的物件對它的依賴都是相同的,接下來將這個場景用程式來實作
1、程式代碼
①、皇帝類
public class Emperor {
// 在皇帝類內部初始化一個皇帝
private static final Emperor emperor = new Emperor();
// 設定構造方法為private防止其它類再生成另一個皇帝
private Emperor(){
}
// 定義外部獲取到該類物件的唯一方法
public static Emperor getInstance(){
return emperor;
}
// 皇帝開始說話了
public static void say(){
System.out.println("我就是皇帝XXX");
}
}
通過定義一個私有訪問權限的建構式,避免被其他類new出來一個物件,而Emperor自己則可以new出一個物件來,其他類對該類的訪問都可以通過getInstance()獲得同一個物件,
②、臣子類
public class Minister {
public static void main(String[] args) {
// 模擬一個上朝的場景,臣子連續三天上朝叩拜皇帝
for (int day = 0;day < 3;day++){
// 記錄臣子第幾天上朝叩拜皇帝
System.out.println("<--------第" + (day + 1) + "天上朝-------->");
// 臣子叩拜皇帝時說的話
System.out.println("吾皇萬歲萬歲萬萬歲");
// 開始獲取到皇帝物件
Emperor emperor = Emperor.getInstance();
// 皇帝開始說話了
emperor.say();
}
}
}
③、運行結果

二、單例模式的定義
單例模式:
Ensure a class has only one instance, and provide a global point of access to it.
(確保某一個類只有一個實體,而且自行實體化并向整個系統提供這個實體)
單例模式屬于創造型別設計模式
1、單例模式通用類圖

類圖決議:
Single類為單例類,通過使用private的建構式確保了在某一個應用中只產生一個實體,(構造方法為private時無法在其他類中通過new關鍵字實體該類的物件),并且在Singleton類中自行完成實體化
自行實體化代碼:
private static final Singleton singleton = new Singleton();
2、單例模式通用代碼
public class Singleton {
// 在類的內部將本類的物件自行實體化
private static final Singleton singleton = new Singleton();
// 將構造方法訪問權限設定為private來限制產生多個物件
private Singleton(){
}
// 外界只能通過該方法獲得實體物件
public static Singleton getSingleton(){
return singleton;
}
// 本類中的其它方法,盡量為static
public static void doSomething(){
}
}
三、單例模式的應用
1、單例模式的優點
- 單例模式在記憶體中只有一個實體,因此減少了記憶體的開支,特別是當一個物件需要頻繁地創建、銷毀而且創建或銷毀時性能又無法優化時單例模式的優勢就很明顯了,
- 由于單例模式值生成一個實體,所以系統的性能開銷被大大的降低,當一個物件的產生需要比較多的資源時,例如讀取配置、產生其他依賴物件時,在可以通過在應用啟動時直接產生一個單例物件,然后用永久駐留記憶體的方式來解決(在JavaEE中采用單例模式時應當注意JVM垃圾回識訓制),
- 單例模式可以避免對資源的多重占用,例如在寫一個檔案時由于記憶體中只有一個實體存在,因此對這一個資源檔案加鎖后可以避免對同一個資源檔案的同時寫操作,
- 單例模式可以在系統設定全域訪問點,優化和共享資源訪問,例如可以設計一個單例類來負責所有資料表的映射處理,
2、單例模式的缺點
- 單例模式一般不是介面,因此擴展很困難,如果要進行擴展的話,除了修改原本類中的代碼外基本上沒有第二種方法可以實作擴展,這個違反了開閉原則(對擴展開放,對修改關閉,意思是可以在類的基礎上擴展代碼,但是不能修改原有的代碼),在這里要注意一個問題:為什么單例模式一般不是介面?答:因為在單例模式中,是要求自行實體化并且提供單一實體,然而介面和抽象類是不可能被實體化的,因此介面和抽象類對單例模式來說沒有任何意義,
- 單例模式對測驗是不友好的,在并行開發的環境中,如果單例模式沒有完成,是不能進行測驗的,
- 單例模式與單一職責原則有沖突,在Java設計模式中一個類應該只實作一個邏輯,而單例模式則將多種業務邏輯融合在了一個類中,因此是否采取單例模式要取決于實際環境,
3、單例模式的使用場景
通常在一個系統中,要求某些類有且僅有一個物件,如果出現多個物件就會出現一些不必要的問題時可以采用單例模式
- 要求生成唯一序列號的環境
- 在整個專案中需要一個共享訪問點或共享資料,例如一個前端頁面上的計數器,不需要把每一次重繪都記錄到資料庫中,使用單例模式保持計數器的值,還可以確保執行緒是安全的,
- 創建一個物件需要消耗的資源過多,例如要訪問IO和資料庫等資源時可以使用單例模式
- 需要定義大量的靜態常量以及靜態方法的環境,例如需要定義大量工具類的環境也可以采用單例模式
4、單例模式的注意事項
①、執行緒不安全的懶漢式單例模式
在非高并發(低并發)的情況下,單例模式不會出現產生多個實體的問題,但是在高并發的情況下,使用單例模式就需要考慮行程同步的問題,
例如下列代碼:
public class Singleton {
// 在類的內部將本類的物件自行實體化
private static Singleton singleton = new Singleton();
// 將構造方法訪問權限設定為private來限制產生多個物件
private Singleton(){
}
// 外界只能通過該方法獲得實體物件
public static Singleton getSingleton(){
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
在這段單例模式的代碼中,如果在高并發的情況下會出現危險,
if (singleton == null){
singleton = new Singleton();
}
原因:
在高并發的場景下,如果一個執行緒A執行到singleton = new Singleton(),但是此時還沒有獲取到物件(物件的初始化是需要時間的),假設在這同一時間第二個執行緒B執行到了(singleton == null)判斷,那么執行緒B獲得的判斷條件也是真,也是單例物件還不存在,于是繼續運行下去,執行緒A和執行緒B都獲得了一個物件,在記憶體中就出現了兩個物件,
因此為了解決這種執行緒不安全的情況,可以在getSingleton方法前加synchronized關鍵字,也可以在getSingleton方法內部增加synchronized關鍵字來實作,
所以就引出了多執行緒安全的兩種單例模式:懶漢式單例模式和餓漢式單例模式
②、執行緒安全的懶漢式單例模式
public class Singleton {
// 在類的內部將本類的物件自行實體化
private static Singleton singleton = new Singleton();
// 將構造方法訪問權限設定為private來限制產生多個物件
private Singleton(){
}
// 外界只能通過該方法獲得實體物件
public static synchronized Singleton getSingleton(){
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
// 本類中的其它方法,盡量為static
public static void doSomething(){
}
}
正如之前提到過解決多執行緒的方法,多執行緒安全的懶漢式單例模式通過加鎖有效的避免了在記憶體中產生多個物件的問題,所以這種懶漢式單例模式的執行緒是安全的,并且第一次呼叫時才初始化,很好的避免了記憶體浪費,但是懶漢式單例模式必須加鎖synchronized才能保證單例,但加鎖會影響效率,
③、餓漢式單例模式
public class Singleton {
// 在類的內部將本類的物件自行實體化
private static Singleton singleton = new Singleton();
// 將構造方法訪問權限設定為private來限制產生多個物件
private Singleton(){
}
// 外界只能通過該方法獲得實體物件
public static Singleton getSingleton(){
return singleton;
}
// 本類中的其它方法,盡量為static
public static void doSomething(){
}
}
餓漢式單例模式在類加載時就初始化,會浪費記憶體,但是相反地,因為餓漢式單例模式沒有加鎖,執行效率會提高,
④、懶漢式單例模式和餓漢式單例模式比較
| 優缺點\模式 | 懶漢式單例模式 | 餓漢式單例模式 |
|---|---|---|
| 優點 | 第一次呼叫才初始化,避免記憶體浪費 | 沒有加鎖,執行效率會提高 |
| 缺點 | 必須加鎖 synchronized 才能保證單例,但加鎖會影響效率 | 類加載時就初始化,浪費記憶體 |
| 多執行緒安全 | 安全 | 安全 |
總結
以上便是Java設計模式中的第一個單例模式,單例模式是23個模式中比較簡單的模式,應用也特別廣泛,例如在Spring中,每個Bean默認就是單例的,這樣做得有點就是Spring容器可以管理這些Bean的宣告周期,可以自由的決定什么時候將Bean創建出來,什么時候銷毀Bean、銷毀的時候要如何處理等,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/281607.html
標籤:java
上一篇:自定義注解,你會了嗎?
下一篇:Java字串處理
