国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

[Android]單例模式

劉玉平 / 1351人閱讀

摘要:即便如此,出于效率考慮,推薦使用雙重校驗鎖和靜態內部類單例模式。

概述

單例模式是應用最廣的模式之一,在應用這個模式時,單例對象的類必須保證只有一個實例存在。許多時候整個系統只需要擁有一個全局對象,這樣有利于我們協調系統整體的行為。如在一個應用中,應該只有一個ImageLoader實例,這個ImageLoader中又含有線程池、緩存系統、網絡請求等,很消耗資源。因此不應該讓它構造多個實例。這樣不能自由構造對象的情況,就是單例模式的使用場景。

定義

確保一個類只有一個實例,而且自行實例化并向整個系統提供這個實例。

使用場景

確保某個類有且只要一個對象的場景,避免產生多個對象消耗過多的資源,或者某種類型的對象只應該有且只有一個。例如,創建一個對象需要消耗的資源過多,如要訪問IO和數據庫等資源,這時就要考慮使用單例模式。

UML類圖

單例模式的UML類圖如下:

角色介紹:

Client:高層客戶端

Singleton:單例類

實現單例模式主要有以下幾個關鍵點:

構造函數不對外開放,一般為private;

通過一個靜態方法或者枚舉返回單例類對象;

確保單例類的對象有且只有一個,尤其是在多線程環境下;

確保單例類對象在反序列化時不會重新構建對象;

單例模式中實現比較困難的是在多線程環境下構造單例類的對象有且只有一個。

簡單示例

單例模式在設計模式中是結構比較簡單的,只有一個單例類,沒有其他層次結構和抽象。該模式需要確保該類只能生成一個對象,通常是該類需要消耗較多的資源或者沒有對個實例的情況。例如一個公司只有一個CEO、一個應用只有一個Application對象等。

下面以公司里的CEO為例來簡單演示一下,一個公司可以有多個VP、無數個員工,但只有一個CEO,代碼如下:

/**
 * 
 * 普通員工
 *
 */
public class Staff {
    
    public void work() {
        //干活
    }


}

//副總裁
public class VP extends Staff {

    @Override
    public void work() {
        // 管理下面的經理
        
    }
}

//CEO,餓漢式單例
public class CEO extends Staff {
    
    private static final CEO mCEO = new CEO();
    
    private CEO() {
        
    }

    //公有的靜態函數,對外暴露獲取單例對象的接口
    public static CEO getCeo() {
        return mCEO;
    }
    @Override
    public void work() {
        // 管理VP
    }
}

//公司類
public class Company {

    private List mStaffs = new ArrayList();
    
    public void addStaff(Staff staff) {
        mStaffs.add(staff);
    }
    
    public void showStaffs() {
        for(Staff staff : mStaffs) {
            System.out.println("Obj: " + staff.toString());
        }
    }
}

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        Company company = new Company();
        
        //CEO對象只能通過getCeo獲取
        Staff ceo1 = CEO.getCeo();
        Staff ceo2 = CEO.getCeo();
        company.addStaff(ceo1);
        company.addStaff(ceo2);
        
        Staff vp1 = new VP();
        Staff vp2 = new VP();
        company.addStaff(vp1);
        company.addStaff(vp2);
        
        Staff staff1 = new Staff();
        Staff staff2 = new Staff();
        
        company.addStaff(staff1);
        company.addStaff(staff2);

        company.showStaffs();
    }

運行輸出結果如下:

Obj: com.liuguoquan.design.single.CEO@15db9742
Obj: com.liuguoquan.design.single.CEO@15db9742
Obj: com.liuguoquan.design.single.VP@6d06d69c
Obj: com.liuguoquan.design.single.VP@7852e922
Obj: com.liuguoquan.design.single.Staff@4e25154f
Obj: com.liuguoquan.design.single.Staff@70dea4e

從上面代碼可以看出,CEO類不能通過new的形式構造函數,只能通過CEO.getCeo()方法來獲取,而這個CEO對象是靜態對象,并且在聲明的時候就已經初始化,這就保證類CEO對象的唯一性。

從輸出結果中可以看出,CEO兩次輸出的CEO對象的地址都一樣,說明是同一個CEO對象;而VP、Staff等類型的對象都是不同的。

實現方式 餓漢式

餓漢式模式是在聲明靜態對象時就已經初始化,這種方式簡單粗暴,如果單例對象初始化非常快,而且占用內存小的時候這種方式是比較適合的,可以直接在應用啟動時加載初始化。實現如下:

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {

    }

    public static Singleton getInstance() {
        return instance;
    }
}
懶漢式

懶漢模式是聲明一個靜態對象,并且在用戶第一次調用getInstance時進行初始化。

public class Singleton {

    private static Singleton instance = null;

    private Singleton() {

    }

    public static Singleton getInstance() {

        synchronized(Singleton.class) {
            if (instance == null) {
                instance = new Singleton();
            }
        return instance;
    }
}

getInstance方法中添加了Synchronized關鍵字,也就是同步類synchronized關鍵字包含的代碼塊,這就是上面所說的在多線程中保證單例對象唯一性的手段。但是仍存在一個問題,即使instance已經初始化,每次調用getInstance方法都會進行同步,這樣會消耗不必要的資源,這也是懶漢式存在的最大問題

懶漢單例模式的優點是只有在使用時才會被實例化,在一定程度上節約了資源,缺點是第一次加載時需要及時進行實例化,反應稍慢,最大問題是每次調用geInstance都進行同步,造成不必要的同步開銷,這樣模式一般不建議使用。

Double CheckLock(雙重校驗鎖)

DCL方式的優點是既能夠在需要時才初始化單例,又能夠保證線程的安全,且單例對象初始化后調用getInstance不獲取同步鎖。

public class Singleton {

    //private static volatile Singleton instance = null;
    private static Singleton instance = null;

    private Singleton() {

    }

    public static Singleton getInstance() {
        //如果已經初始化,不需要每次獲取同步鎖
        if(instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
        }
        return instance;
    }
}

可以看到getInstance方法對instance進行了兩次判空:第一層判斷主要是為了避免不必要的同步,第二層判斷主要則是為了在null的情況下創建實例。下面,我們來分析一下:

假設線程A執行到instance=new Singleton()語句,這里看起來是一句代碼,但實際上它并不是一個原子操作,這局代碼最終會被編譯成多條匯編指令,它大致做了3件事情:

給Singleton的實例分配內存

調用Singleton()的 構造函數,初始化字段成員

將instance對象執行分配的內存空間(此時instance就不是null了)

但是,由于Java編譯器運行處理器亂序執行,以及jdk1.5之前Java內存模型中Cache、寄存器到主內存會寫順序的規定,上面的第二和第三的順序是無法保證的。也就是說,執行順序可能是1-2-3也可能是1-3-2.如果是后者,并且在3執行完畢、2未執行之前,被切換到線程B上,這時候instance因為已經在線程A內執行3了,instance已經是非null,所有線程B直接取走instance,再使用時就會出錯,這就是DCL失效問題,而且這種難以跟蹤難以重現的問題很可能會隱藏很久。

在jdk1.5之后,官方已經注意到這種問題,調整了JMM、具體化了volatile關鍵字,因此,如果是1.5或之后的版本,只需要將instance的定義改成private static volatile Singleton instance = null;就可以保證instance對象每次都是從主內存中讀取,就可以使用DCL的寫法來完成單例模式。當然,volatile多少會影響到性能,但考慮到程序的正確性,犧牲這點性能還是值得的。

DCL的優點:資源利用率高,第一次執行getInstance時單例對象才會被實例化,效率高。

缺點:第一次加載稍慢,也由于Java內存模型的原因偶爾會失敗。在高并發的環境下也有一定的缺陷,雖然概率發生很小。

DCL模式是使用最多的單例實現模式,它能夠在需要時才實例化單例對象,并且能夠在絕大多數場景下保證單例對象的唯一性,除非你的代碼在并發場景比較復雜或者低于jdk1.6版本下使用,否則這種方式一般能夠滿足需求。

靜態內部類單例模式

在《Java并發編程實戰》中談到不贊成使用DCL的優化方式,而建議使用如下代碼替代:

public class Singleton {

    private Singleton() {}

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

    //靜態內部類
    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
}

當第一次加載Singleton類時并不會初始化instance,只有第一次調用Singleton的getInstance方法時才會導致instance被初始化。因此,第一次調用getInstance方法會導致虛擬機加載SingletonHolder類,這種方式不僅能夠確保線程安全,也能夠保證單例對象的唯一性,同時也延遲了單例的實例化,所以這是推薦使用的單例模式實現方式。

枚舉單例
public enum Singleton {

    //定義一個枚舉的元素,它就是Singleton的一個實例
    INSTANCE;

    public void doSomething() {

    }
}

//使用
public static void main(String[] args){
   Singleton singleton = Singleton.instance;
   singleton.doSomething();
}

寫法簡單是枚舉單例最大的優點,枚舉在Java中與普通的類是一樣的,不僅能夠有字段,還能夠有自己的方法。最重要的是默認枚舉實例的創建時線程安全的,并且在任何情況下它都是一個單例。

為什么這么說呢?在上述的幾種單例模式實現中,在一個情況下它們會出現重新創建對象的情況,那就是反序列化。

通過序列化可以將一個單例的實例對象寫到磁盤,然后再讀回來,從而有效地獲得一個實例。即使構造函數時私有的,反序列化時依然可以通過特殊的途徑去創建類的一個新的實例,相當于調用該類的構造函數。反序列化操作提供一個很特別的鉤子函數,類中具有一個私有的、被實例化的方法readResolve(),這個方法可以讓開發人員控制對象的反序列化。例如,上述幾個實例中如果要杜絕單例對象在被反序列化時重新生成對象,那么必須加入如下方法:

private Object readResolve() throws ObjectStreamException {
    return instance;
}

也就是在readResolve方法中將instance對象返回,而不是默認的重新生成一個新的對象。而對于枚舉并不存在這樣的問題,因為即使反序列化它也不會重新生成新的實例。

容器管理單例
public class SingletonManager {

    private static Map objMap = new HashMap();

    public static void registerService(String key,Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key);
        }
    }

    public static getInstance(String key) {
        return objMap.get(key);
    }
}

在程序的初始,將多種單例類注入到一個統一的管理類中,在使用根據key獲取對應類型的對象,這種方式使得我們可以管理很多類型的單例,并且在使用它們的時候可以通過統一的接口進行獲取操作操作,降低用戶的使用成本,也對用戶隱藏了具體實現,降低了耦合度。

總結

單例模式是運用頻率很高的模式,但是,由于在客戶端通常沒有高并發的情況,因此,選擇哪種實現方式并不會有太大的影響。即便如此,出于效率考慮,推薦使用雙重校驗鎖和靜態內部類單例模式。

優點

由于單例模式在內存中只有一個實例,減少了內存開支,特別是一個對象需要頻繁創建、銷毀時,而且創建或者銷毀時性能又無法優化,單例模式的優勢就非常明顯。

由于單例模式只生成一個實例,所以,減少了系統的性能開銷,當一個對象的產生需要比較多的資源時,如讀取配置、產生依賴對象時,則可以通過在應用啟動時直接產生一個單例對象,然后用永駐內存的方式解決。

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

單例模式可以在系統設置全局訪問點,優化和共享資源訪問,例如,可以設計一個單例類,負責所有數據表的映射處理。

缺點:

單例模式一般沒有接口,擴展很困難,若要擴展,除了修改代碼基本沒有第二種途徑可以實現。

在Android中,單例對象如果持有Context,那么很容易引發內存泄露,此時需要注意傳給單例對象的Context最好是Application Context。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/66125.html

相關文章

  • Android中的設計模式單例模式

    摘要:總結單例是運用頻率很高的模式,因為客戶端沒有高并發的情況,選擇哪種方式并不會有太大的影響,出于效率考慮,推薦使用和靜態內部類實現單例模式。 單例模式介紹 單例模式是應用最廣的模式之一,也可能是很多人唯一會使用的設計模式。在應用單例模式時,單例對象的類必須保證只用一個實例存在。許多時候整個系統只需要一個全局對象,這樣有利于我么能協調整個系統整體的行為。 單例模式的使用場景 確保某個類有且...

    yzd 評論0 收藏0
  • Java單例模式實現

    摘要:所以,在版本前,雙重檢查鎖形式的單例模式是無法保證線程安全的。 單例模式可能是代碼最少的模式了,但是少不一定意味著簡單,想要用好、用對單例模式,還真得費一番腦筋。本文對Java中常見的單例模式寫法做了一個總結,如有錯漏之處,懇請讀者指正。 餓漢法 顧名思義,餓漢法就是在第一次引用該類的時候就創建對象實例,而不管實際是否需要創建。代碼如下: public class Singleton...

    jaysun 評論0 收藏0
  • 求職準備 - 收藏集 - 掘金

    摘要:一基礎接口的意義百度規范擴展回調抽象類的意義想不想通過一線互聯網公司面試文檔整理為電子書掘金簡介谷歌求職記我花了八個月準備谷歌面試掘金原文鏈接翻譯者 【面試寶典】從對象深入分析 Java 中實例變量和類變量的區別 - 掘金原創文章,轉載請務必保留原出處為:http://www.54tianzhisheng.cn/... , 歡迎訪問我的站點,閱讀更多有深度的文章。 實例變量 和 類變量...

    cuieney 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<