抽絲剝繭 細說架構那些事——【優銳課】
單例模式可確保在給定的時間實例中只能創建一個具有全局訪問點的對象。這是面向對象編程中最常用的技術之一。盡管它很簡單,但從類設計的角度來看可能是最簡單的,但是在嘗試實現它們之前,必須先解決一些細微的問題。本文是在學習完優銳課JAVA架構VIP課程—【框架源碼專題】中《學習源碼中的優秀設計模式》后寫下的學習感悟。通過引用Java代碼示例來深入探索這種模式。
總覽
在某些情況下,系統應在給定的時間點僅允許一個類的對象存儲在內存中。這意味着,當程序實例化該對象時,不應允許該程序創建該類的其他對象。例如,在連接到數據庫的系統中,僅使用一個對象來管理數據庫連接。這樣可以確保其他對象無法初始化不必要的連接,從而由於多個實例化而使系統的整體性能下降。通過創建多個JDBC連接並觀察性能,可以很容易地對其進行測試。性能肯定會受到影響或顯着降低。單例模式實質上可以保證系統僅創建一個類的實例。
單例模式
使用單例模式是確保給定類實例化一個且只有一個對象的一種標准技術。這是“四人幫”討論的二十四個設計模式之一。想法是解決諸如實例化類的單個實例,訪問類的唯一實例或控制類實例化過程的問題。實現此目的的關鍵技術是通過使用私有修飾符隱藏構造函數,並提供一種應用檢查和驗證的方法,以確保僅創建該類的一個實例。
這很簡單,但有效地滿足了需求。實際上,通過應用相同的原理,我們可以控制創建類的實例的確切數目,而不僅限於單個實例。但是,這里我們僅關注單個實例。
使用單例模式
不難發現實際上我們只需要一個類的一個實例在內存中的情況。多個對象可能會破壞原因或對正在運行的應用程序的性能造成破壞。在面向對象的編程中,正在運行的應用程序通常在內存中具有許多對象,它們在執行過程中相互作用並扮演着至關重要的角色。但是,在實現線程池,高速緩存,對話框或負責首選項和注冊表設置的對象時,數據庫連接,日志記錄,充當外圍設備或圖形卡設備驅動程序的對象等都是我們想要的情況。對象運行的單個實例;否則,它將產生很多問題,例如資源阻塞,內存過載以及許多其他不一致的行為。這正是我們需要確定適合於實現單例模式的類並進行相應設計的地方。
實現單例模式
這是該模式的快速實現。
1 public final class Singleton { 2 private static final Singleton singleInstance = 3 new Singleton(); 4 private Singleton(){ 5 } 6 public static Singleton getInstance(){ 7 return singleInstance; 8 } 9 } 10 11 public class TestSingleton { 12 public static void main(String[] args){ 13 Singleton s1; 14 Singleton s2; 15 s1 = Singleton.getInstance(); 16 s2 = Singleton.getInstance(); 17 if(s1 == s2){ 18 System.out.println("References to same 19 Singleton object"); 20 } 21 } 22 }
Singleton類被聲明為final,因此無法創建任何子類。這甚至限制了多重實例化,即使通過對該類進行子類化。構造函數被聲明為私有;結果,只有Singleton類可以使用此構造函數創建Singleton對象。對Singleton對象的靜態引用將調用私有構造函數,並僅提供該類的一個實例。調用getInstance() 方法時,僅接收對象的參考副本。
在這里,我們通過創建另一個稱為TestSingleton的類來使用一個簡單的測試。此類聲明兩個參考Singleton對象,並調用getInstance()方法。然后,我們比較這兩個引用,以確保它們實際上引用的是同一運行時實例,而不是兩個不同的對象。
有時,我們需要一個僅在第一個靜態方法被調用時才創建實例的實現,而不是在此之前。在前面的示例中,對象是靜態創建的;getInstance()方法的作用是簡單地返回引用。在這種情況下,我們希望在首次調用時創建對象,稱為延遲初始化。另外,我們希望調用是線程安全的;否則,可能導致奇怪的不穩定行為或內存泄漏,從而導致JVM崩潰。這是實現此目的的示例。
1 public final class Singleton { 2 private static volatile Singleton singleInstance = 3 null; 4 private Singleton(){ 5 } 6 public static Singleton getInstance(){ 7 if (singleInstance == null) { 8 synchronized(Singleton.class) { 9 if (singleInstance == null) { 10 singleInstance = new Singleton(); 11 } 12 } 13 } 14 return singleInstance; 15 } 16 } 17 18 public class TestSingleton { 19 public static void main(String[] args){ 20 Singleton s1; 21 Singleton s2; 22 s1 = Singleton.getInstance(); 23 s2 = Singleton.getInstance(); 24 if(s1 == s2){ 25 System.out.println("References to same 26 Singleton object"); 27 } 28 } 29 }
也可以使用Enum實現單例。實際上,在可能的情況下,最好使用Enum代替類來實現單例模式。JVM保證從單例Enum中只能創建一個實例。
1 public enum SingletonEnum { 2 SINGLEINSTANCE; 3 public void someMethod(){ 4 // ... 5 } 6 } 7 8 public class TestSingleton { 9 public static void main(String[] args){ 10 SingletonEnum s1; 11 SingletonEnum s2; 12 s1 = SingletonEnum.SINGLEINSTANCE; 13 s2 = SingletonEnum.SINGLEINSTANCE; 14 if (s1 == s2){ 15 System.out.println("References to same 16 Singleton object"); 17 } 18 } 19 }
結論
與往常一樣,用戶的自由裁量權是良好設計的關鍵,因為不當使用單例模式可能會導致其他問題。如果單例執行復雜且重量級的操作,則很難對其進行測試。更好的建議是使用依賴項注入框架來構造單個對象。在諸如在GUI應用程序中創建對話框或很少有並發用戶的情況下,單例效果最佳。單例模式雖然有用,但在大型可擴展應用程序中造成性能瓶頸方面臭名昭著。
感謝閱讀!歡迎留言。想更深入探討學習也歡迎私信我。下篇繼續~