設計模式之單例模式的幾種常見寫法

  1. 單例模式:一個類確保在全局中只有一個實例存在。
    有時候我們需要一個類只能有一個實例,否則可能出現數據不同步的情況;有時我們需要考慮到
    性能的優化,當創建過多的對象,會消耗很多的資源,造成不必要的資源浪費,這時就可以考慮
    使用單例。
  2. 那麼怎麼去實現以及確保一個類是單例的呢?
    單例模式要注意一下幾點

(1) 私有化(private)構造函數,使得客戶端不能通過new的形式來創建單例類實例。

(2) 通過一個靜態方法或者枚舉返回單例類對象。

(3) 確保單例類對象在全局中只有一個實例,特別是在多線程環境中。

  1. 常見的單例模式創建方法
    假設有這麼一個類HttpHelper,用於網絡請求的封裝,我們要求該類爲單例模式。我們的寫法可以如下:
(1) 餓漢單例模式,聲明靜態對象時就初始化

public class HttpHelper {
    //注意這裏有 final 修飾
    private static final HttpHelper mHttpHelper = new HttpHelper();

    //私有化構造函數
    private HttpHelper() {
    }

    //公有的,對外暴露獲取單例的方法
    public static HttpHelper getHttpHelper() {
        return mHttpHelper;
    }
}


(2) 懶漢單例模式,先聲明一個靜態對象,當用戶第一次調用時初始化

public class HttpHelper {
    private static HttpHelper instance;

    //私有化構造函數
    private HttpHelper() {
    }

    //公有的,對外暴露獲取單例的方法
    public static HttpHelper getInstance() {
        if (instance == null) {
            instance = new HttpHelper();
        }
        return instance;
    }
}


(3) 雙重檢查鎖模式(Double Check Lock,DCL)

這種方式是既能夠在需要的時候才初始化單例,又能夠保證線程安全,且單例對象

初始化後再調用getInstance 不進行同步鎖。看實例代碼

public class HttpHelper {
    private static HttpHelper instance;

    //私有化構造函數
    private HttpHelper() {
    }

    //公有的,對外暴露獲取單例的方法
    public static HttpHelper getInstance() {
        if (instance == null) {
            synchronized (HttpHelper.class) {
                if (instance == null) {
                    instance = new HttpHelper();
                }
            }
        }
        return instance;
    }
}

我基本都是用這種模式的單例。

(4) 靜態內部類單例

DCL 在某些情況下會失效,因此有人建議使用以下這種方式。


public class HttpHelper {
    //私有化構造函數
    private HttpHelper() {
    }

    //公有的,對外暴露獲取單例的方法
    public static HttpHelper getInstance() {
        return HttpHelperHolder.sInstance;
    }

    //靜態內部類
    private static class HttpHelperHolder{
        private static final HttpHelper sInstance = new HttpHelper();
    }
}

靜態內部類的優勢:當第一次加載HttpHelper類時並不會初始化sInstance,只有在第一次調用
getInstance()方法時纔會初始化sInstance。也就是說在調用第一次調用getInstance()方法的
時候,纔會去加載內部類HttpHelperHolder,這保證了線程安全,也能保證單例對象的唯一性,同
時延遲了單例的實例化,況且寫法也不必其他方式複雜。所以可以考慮這種方式。

(5) 枚舉單例

public enum HttpHelper {
    INSTANCE;
}

這個是最簡單的,而且可以避免在反序列化的情況下重新創建對象的情況。在jdk5.0纔出現的枚舉,現
在很多人都建議用這種方式來創建單例。很顯然寫法簡單是他的優點。

以上幾種方式是用得最多的單例模式寫法。
  1. 總結下單例模式的優缺點:

(1) 單例模式在內存中只有一個實例,減少了內存開支,特別是一個對象需要頻繁地創建、銷燬時,而且創建或銷燬時性能又無法優化,單例

模式的優勢就很明顯。

(2) 單例模式在全局只生成一個實例,減少了系統的性能開銷,當一個對象的產生需要比較多的資源時,如讀取配置、生產其他依賴對象時,則

可以通過在應用啓動時直接生成一個單例對象,然後用永久駐留內存的方式來解決。

(3) 單例模式可以避免對資源的多重佔用,例如一個寫文件操作,由於只有一個實例存在內存中,避免了對同一個資源的同時寫操作。

(4) 單例模式可以在系統設置全局的訪問點,優化和共享資源訪問。

缺點:

(1) 單例模式沒有接口,想要擴展只有修改代碼這一條路。

(2) 單例對象如果持有Context,很容易引發內存泄漏,因此,我們傳給單例對象的Context最好是Application Context。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章