手撕设计模式之「单例模式」(详细解析)
前言
單例模式主要用來保證系統中某個類的實例對象的唯一性,是最簡單的一種設計模式,而且在面試中也經常會被問到,是非常值得我們去學習的。如果你們面試遇到了哪些設計模式的考察,也歡迎留言,我會及時發新的博文。
文章目錄
- 前言
- 1. 模式定義
- 2. 模式實現
- 3. 單例模式的拓展
- 3.1 懶漢式單例
- 3.2 餓漢式單例
- 4. 多線程環境中的單例模式
- 4.1 延時加載
- 4.2 雙重校驗鎖
- 4.3 靜態內部類
- 參考資料
1. 模式定義
單例模式(Singleton Pattern):確保某一個類只有一個實例,而且自行實例化并像整個系統提供這個實例,這個類稱為單例類,它會提供全局訪問的方法。單例模式是對象創建型模式的一種。
單例模式的三個要點:
- 某個類只能有一個實例
- 必須自行創建這個實例
- 必須自行向整個系統提供這個實例
2. 模式實現
要想實現單例模式,我們可以從它的三個要點入手:
首先是單例的唯一性,既然單例是唯一的,我們毫無疑問應該給他加上 static 關鍵字,又因為這個對象不應該直接暴露,所以還要加上 private 進行訪問限定。
private static Singleton instince = null;接著,就是這個對象需要由單例類自行創建,這時我們就應該屏蔽外界訪問該類初始化方法的接口,也就是用 private 修飾構造函數。
private Singleton() { }最后一點就比較容易實現了,給公有工廠方法 static 關鍵字,即可實現向整個系統提供這個實例。
public static Singleton getInstance() {if (instance == null) {instince = new Singleton();}return instance; }將以上代碼整合一下,就可以得到如下的單例模式的代碼實現模板:
public class Singleton {//靜態私有成員變量,保證實例的唯一性private static Singleton instince = null;//私有構造函數,保證實例由類自行創建private Singleton() {}// 靜態公有工廠方法,向系統提供這個唯一實例public static Singleton getInstance() {if (instance == null) {instince = new Singleton();}return instance;} }下面是針對上面單例模式實現模板的客戶端測試代碼:
public Client {public static void main(String []args) {Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2); //true} }3. 單例模式的拓展
單例模式可以分為懶漢式單例和餓漢式單例,它們的區別主要在單例類對象的初始化時間上。下面我們來詳細講解:
3.1 懶漢式單例
懶漢式單例模式的結構圖如下圖所示:
在模式圖中,我們可以看到這實例對象在創建類的時候并沒有初始化,而是等到有人調用getInstance()方法獲取實例的時候才進行實例對象的初始化,這也恰恰體現了懶漢式的這種“懶惰行為”。
下面是以“身份證號碼類”作為例子,編寫的懶漢式單例代碼。
public class IdentityCardNo {private String no;//某個類只能有一個實例private static IdentityCardNo instince = null;//必須自行創建這個實例private IdentityCardNo() {}//必須自行向整個系統提供這個實例public static IdentityCardNo getInstance() {if (instince == null) {System.out.println("第一次辦理身份證,分配新號碼");instince = new IdentityCardNo();instince.setIdentityCardNo("400000199710301111");} else {System.out.println("重復辦理身份證,獲取舊號碼");}return instince;}public String getIdentityCardNo() {return no;}public void setIdentityCardNo(String no) {this.no = no;} }可以仿造上面的方法,寫一個Client類來進行檢驗。
3.2 餓漢式單例
餓漢式單例模式的結構圖如下圖所示:
由于餓漢式單例的“饑餓”特性,使得它在類加載階段就對單例類對象進行了初始化,從上面的結構圖也可以看出這一點。
下面代碼是上面那個例子的餓漢式實現:
public class IdentityCardNo {private String no;//某個類只能有一個實例private static IdentityCardNo instince = new IdentityCardNo();//必須自行創建這個實例private IdentityCardNo() {System.out.println("第一次辦理身份證,分配新號碼");}//必須自行向整個系統提供這個實例public static IdentityCardNo getInstance() {instince.setIdentityCardNo("400000199710301111");return instince;}public String getIdentityCardNo() {return no;}public void setIdentityCardNo(String no) {this.no = no;} }4. 多線程環境中的單例模式
上面提到的懶漢式單例是線程不安全的,在多線程的環境下,對象的唯一性得不到保障。于是就有了下面幾種線程安全的單例模式實現方法,其中第1,2種方法是對懶漢式的改進,第 3 中是對餓漢式的改進。
4.1 延時加載
我們可以用synchronized關鍵字修飾getInstance()方法,利用延時加載的方式保證在多線程環境下對象的唯一性。但是這種方法在容易造成線程擁塞,效率不高。
實現代碼如下:
public class Singleton {private static Singleton instince = null;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instince = new Singleton();}return instance;} }4.2 雙重校驗鎖
這種方式采用雙鎖機制,線程安全且在多線程情況下能保持高性能,是比較推薦使用的方法。
實現代碼如下:
public class Singleton {private static Singleton instince = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {//只將synchronized關鍵字用在了初始化模塊synchronized (Singleton.class) {if (install == null) {instince = new Singleton();}}}return instance;} }4.3 靜態內部類
這種方式通過給對象加上final關鍵字修飾,能達到雙檢鎖方式一樣的功效,但實現更簡單。對靜態域使用延遲初始化,應使用這種方式而不是雙檢鎖方式。這種方式只適用于靜態域的情況,雙檢鎖方式可在實例域需要延遲初始化時使用。
實現代碼如下:
public class Singleton {private static final Singleton instince = new Singleton();private Singleton() {}public static Singleton getInstance() {return Singleton.instance;} }參考資料
總結
以上是生活随笔為你收集整理的手撕设计模式之「单例模式」(详细解析)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 剑指Offer #14 链表中倒数第k个
- 下一篇: XML简介及基本语法