Java設(shè)計(jì)模式之單例模式及在Android中的重要使用

之前在開發(fā)中老用到一些設(shè)計(jì)模式可是呢又不是很懂,于是狠下心來琢磨一番金砍。下面是我琢磨后總結(jié)的局蚀,希望對(duì)您有用。如果發(fā)現(xiàn)了問題捞魁,請(qǐng)幫忙指正至会。

一、單例模式是什么谱俭?

單例模式最初的定義出現(xiàn)于《設(shè)計(jì)模式》:“保證一個(gè)類僅有一個(gè)實(shí)例奉件,并提供一個(gè)訪問它的全局訪問點(diǎn)±ブ”
  Java中單例模式定義县貌;“一個(gè)類有且僅有一個(gè)實(shí)例,并且自行實(shí)例化向整個(gè)系統(tǒng)提供該實(shí)例凑懂∶汉郏”

二、為什么用單例模式接谨?

對(duì)于系統(tǒng)中的某些類來說摆碉,只有一個(gè)實(shí)例很重要。例如脓豪,一個(gè)系統(tǒng)中可以存在多個(gè)打印任務(wù)巷帝,但是只能有一個(gè)正在工作的任務(wù);一個(gè)系統(tǒng)只有有一個(gè)窗口管理器或文件系統(tǒng)扫夜;一個(gè)系統(tǒng)只能有一個(gè)計(jì)時(shí)工具或ID生成器楞泼。如在Windows OS 中就只能打開一個(gè)任務(wù)管理器。如果不使用機(jī)制對(duì)窗口對(duì)象進(jìn)行唯一化笤闯,將彈出多個(gè)窗口堕阔,如果這些窗口顯示的內(nèi)容完全一致,則重復(fù)對(duì)象颗味,浪費(fèi)內(nèi)存資源超陆;如果這些窗口顯示的內(nèi)容不一致,則意味著某一瞬間系統(tǒng)有多個(gè)狀態(tài)脱衙,與實(shí)際不符侥猬,也會(huì)為用戶帶來誤解例驹,不知道哪一個(gè)才是真實(shí)的狀態(tài)。因此有時(shí)確保系統(tǒng)中某個(gè)對(duì)象的唯一性即一個(gè)類只能有一個(gè)實(shí)例是非常重要的退唠。
  如何保證一個(gè)類只有一個(gè)實(shí)例并且這個(gè)實(shí)例易于被訪問呢鹃锈?定義一個(gè)全局變量可以確保對(duì)象隨時(shí)都可以被訪問,但不能防止我們實(shí)例化多個(gè)對(duì)象瞧预。一個(gè)更好的解決辦法是讓類自身負(fù)責(zé)保存它的唯一實(shí)例屎债。這個(gè)類可以保證沒有其他實(shí)例被創(chuàng)建,并且它可以提供一個(gè)訪問該實(shí)例的方法垢油。這就是單例模式的模式動(dòng)機(jī)盆驹。

三、單例模式特點(diǎn)

單例模式特點(diǎn)有三個(gè)
1滩愁、單例類只能有一個(gè)實(shí)例躯喇。
2、單例類必須自己創(chuàng)建自己的唯一實(shí)例硝枉。
3廉丽、單例類必須給其他對(duì)象(整個(gè)系統(tǒng))提供這一實(shí)例。
 從具體實(shí)現(xiàn)角度分析妻味,一是單例模式的類只提供私有的(private)構(gòu)造函數(shù)正压,二是類定義中含有一個(gè)該類的靜態(tài)私有(private static)對(duì)象,三是該類提供了一個(gè)靜態(tài)的(static)公有的(public)函數(shù)用于創(chuàng)建或獲取它本身的靜態(tài)私有對(duì)象责球。

四焦履、Java中幾種常見單例模式寫法

通過上面的介紹你是不是對(duì)單例模式有了一個(gè)總的概念?沒有雏逾,那接下來繼續(xù)給你們放大招嘉裤。
  基于單例模式特點(diǎn),單例對(duì)象通常作為程序中存放配置信息的載體(想想Android中的Application經(jīng)常在里面做一些配置的初始化)栖博,因?yàn)樗軌虮WC其他對(duì)象讀取到一致的信息价脾。例如在某個(gè)服務(wù)器程序中,該服務(wù)器的配置信息可能存放在數(shù)據(jù)庫或 文件中笛匙,這些配置數(shù)據(jù)由某個(gè)單例對(duì)象統(tǒng)一讀取,服務(wù)進(jìn)程中的其他對(duì)象如果要獲取這些配置信息犀变,只需訪問該單例對(duì)象即可妹孙。這種方式極大地簡(jiǎn)化了在復(fù)雜環(huán)境 下,尤其是多線程環(huán)境下的配置管理获枝,但是隨著應(yīng)用場(chǎng)景的不同蠢正,也可能帶來一些同步問題。

1省店、餓漢式單例
//餓漢式單例類.在類初始化時(shí)嚣崭,已經(jīng)自行實(shí)例化 
 public class Singleton {
     //私有的默認(rèn)構(gòu)造子
     private Singleton() {}
     //已經(jīng)自行實(shí)例化 
     private static final Singleton single = new Singleton();
     //靜態(tài)工廠方法 
     public static Singleton getInstance() {
         return single;
     }
 }

上面例子中笨触,在這個(gè)類被加載時(shí),靜態(tài)變量single會(huì)被初始化雹舀,此時(shí)類的私有構(gòu)造子會(huì)被調(diào)用芦劣。這時(shí)單例類的唯一實(shí)例就被構(gòu)造出來了。
  餓漢式其實(shí)是一種比較形象的稱謂说榆。既然餓虚吟,那么在創(chuàng)建對(duì)象實(shí)例的時(shí)候就比較著急,餓了嘛签财,于是在裝載類的時(shí)候就創(chuàng)建對(duì)象實(shí)例--->

private static final Singleton single = new Singleton();

餓漢式是典型的空間換時(shí)間串慰,當(dāng)類裝載時(shí)就會(huì)創(chuàng)建類的實(shí)例,不管你用不用唱蒸,先創(chuàng)建出來邦鲫,然后每次調(diào)用的時(shí)候,就不需要再判斷神汹,節(jié)省了運(yùn)行時(shí)間庆捺。

2、懶漢式單例
//懶漢式單例類.在第一次調(diào)用的時(shí)候?qū)嵗约?  
public class Singleton {  
    private Singleton() {}  
    private static Singleton single=null;  
    //靜態(tài)工廠方法   
    public static synchronized Singleton getInstance() {  
         if (single == null) {    
             single = new Singleton();  
         }    
        return single;  
    }  
}  

上面的懶漢式單例類實(shí)現(xiàn)里對(duì)靜態(tài)工廠方法使用了同步化慎冤,以處理多線程環(huán)境疼燥。
  懶漢式其實(shí)是一種比較形象的稱謂。既然懶蚁堤,那么在創(chuàng)建對(duì)象實(shí)例的時(shí)候就不著急醉者。會(huì)一直等到馬上要使用對(duì)象實(shí)例 的時(shí)候才會(huì)被創(chuàng)建,懶人嘛披诗,總是推脫不開的時(shí)候才會(huì)真正執(zhí)行工作撬即,因此在裝載對(duì)象的時(shí)候不創(chuàng)建對(duì)象實(shí)例。

private static Singleton single=null;  

懶漢式是典型的時(shí)間換空間,就是每次獲取實(shí)例都會(huì)進(jìn)行判斷呈队,看是否需要?jiǎng)?chuàng)建實(shí)例剥槐,浪費(fèi)判斷的時(shí)間。當(dāng)然宪摧,如果一直沒有人使用的話粒竖,那就不會(huì)創(chuàng)建實(shí)例,則節(jié)約內(nèi)存空間
  由于懶漢式的實(shí)現(xiàn)是線程安全的几于,這樣會(huì)降低整個(gè)訪問的速度蕊苗,而且每次都要判斷。那么有沒有更好的方式實(shí)現(xiàn)呢沿彭?

3朽砰、雙重檢查加鎖

可以使用“雙重檢查加鎖”的方式來實(shí)現(xiàn),就可以達(dá)到實(shí)現(xiàn)線程安全,又能使性能不受很大影響瞧柔。
  雙重檢查加鎖:并不是每次進(jìn)入getInstance()都需要同步漆弄,而是先不同步,進(jìn)入方法后造锅,先檢查單例對(duì)象是否存在撼唾,如果不存在才進(jìn)行下面的同步塊,這是第一重檢查备绽,進(jìn)入同步塊后券坞,再次檢查實(shí)例是否存在,如果不存在肺素,就在同步的情況下創(chuàng)建一個(gè)實(shí)例(單例對(duì)象),這是第二重檢查恨锚。這樣就只需要同步一次,從而減輕了多次在同步情況下進(jìn)行判斷所浪費(fèi)的時(shí)間倍靡。
  “雙重檢查加鎖”機(jī)制的實(shí)現(xiàn)會(huì)使用關(guān)鍵字volatile猴伶,它的意思是:被volatile修飾的變量的值,將不會(huì)被本地線程緩存塌西,所有對(duì)該變量的讀寫都是直接操作共享內(nèi)存他挎,從而確保多個(gè)線程能正確的處理該變量。不清楚volatile的看過來volatile解析
代碼實(shí)例:

public class Singleton {
    private volatile static Singleton instance = null;
    private Singleton(){}
    public static Singleton getInstance(){
        //先檢查實(shí)例是否存在捡需,如果不存在才進(jìn)入下面的同步塊
        if(instance == null){
            //同步塊办桨,線程安全的創(chuàng)建實(shí)例
            synchronized (Singleton.class) {
                //再次檢查實(shí)例是否存在,如果不存在才真正的創(chuàng)建實(shí)例
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

這種實(shí)現(xiàn)方式既可以實(shí)現(xiàn)線程安全地創(chuàng)建實(shí)例站辉,而又不會(huì)對(duì)性能造成太大的影響呢撞。它只是第一次創(chuàng)建實(shí)例的時(shí)候同步,以后就不需要同步了饰剥,從而加快了運(yùn)行速度殊霞。
  提示:由于volatile關(guān)鍵字可能會(huì)屏蔽掉虛擬機(jī)中一些必要的代碼優(yōu)化,所以運(yùn)行效率并不是很高汰蓉。因此一般建議绷蹲,沒有特別的需要,不要使用顾孽。也就是說祝钢,雖然可以使用“雙重檢查加鎖”機(jī)制來實(shí)現(xiàn)線程安全的單例,但并不建議大量采用若厚,可以根據(jù)情況來選用太颤。
  根據(jù)上面的分析,常見的兩種單例實(shí)現(xiàn)方式都存在小小的缺陷盹沈,那么有沒有一種方案,既能實(shí)現(xiàn)延遲加載,又能實(shí)現(xiàn)線程安全呢乞封?那就是下面一種方法做裙,放大招了,接著呦肃晚。

4锚贱、靜態(tài)內(nèi)部類
public class Singleton {
    
    private Singleton(){}
    /**
     *    類級(jí)的內(nèi)部類,也就是靜態(tài)的成員式內(nèi)部類关串,該內(nèi)部類的實(shí)例與外部類的實(shí)例
     *    沒有綁定關(guān)系拧廊,而且只有被調(diào)用到時(shí)才會(huì)裝載,從而實(shí)現(xiàn)了延遲加載晋修。
     */
    private static class SingletonHolder{
        /**
         * 靜態(tài)初始化器吧碾,由JVM來保證線程安全
         */
        private static Singleton instance = new Singleton();
    }
    
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

當(dāng)getInstance方法第一次被調(diào)用的時(shí)候,它第一次讀取SingletonHolder.instance墓卦,導(dǎo)致SingletonHolder類得到初始化倦春;而這個(gè)類在裝載并被初始化的時(shí)候,會(huì)初始化它的靜態(tài)域落剪,從而創(chuàng)建Singleton的實(shí)例睁本,由于是靜態(tài)的域,因此只會(huì)在虛擬機(jī)裝載類的時(shí)候初始化一次忠怖,并由虛擬機(jī)來保證它的線程安全性呢堰。

5、單例和枚舉
public enum Singleton {
    /**
     * 定義一個(gè)枚舉的元素凡泣,它就代表了Singleton的一個(gè)實(shí)例枉疼。
     */
    
    uniqueInstance;
    
    /**
     * 單例可以有自己的操作
     */
    public void singletonOperation(){
        //功能處理
    }
}

按照《高效Java 第二版》中的說法:?jiǎn)卧氐拿杜e類型已經(jīng)成為實(shí)現(xiàn)Singleton的最佳方法。用枚舉來實(shí)現(xiàn)單例非常簡(jiǎn)單问麸,只需要編寫一個(gè)包含單個(gè)元素的枚舉類型即可往衷。

對(duì)我來說,我比較喜歡第一種和第四種方式严卖,簡(jiǎn)單易懂席舍。而且在JVM層實(shí)現(xiàn)了線程安全(如果不是多個(gè)類加載器環(huán)境)。一般的情況下哮笆,我會(huì)使用第一種方式来颤,只有在要明確實(shí)現(xiàn)lazy loading效果時(shí)才會(huì)使用第四種方式

五、Android中典型的單例模式Application類

1稠肘、Application是什么福铅?

Application和Activity,Service一樣,是android框架的一個(gè)系統(tǒng)組件,當(dāng)android程序啟動(dòng)時(shí)系統(tǒng)會(huì)創(chuàng)建一個(gè) application對(duì)象项阴,用來存儲(chǔ)系統(tǒng)的一些信息滑黔。通常我們是不需要指定一個(gè)Application的,這時(shí)系統(tǒng)會(huì)自動(dòng)幫我們創(chuàng)建,如果需要?jiǎng)?chuàng)建自己 的Application略荡,也很簡(jiǎn)單創(chuàng)建一個(gè)類繼承 Application并在manifest的application標(biāo)簽中進(jìn)行注冊(cè)(只需要給Application標(biāo)簽增加個(gè)name屬性把自己的 Application的名字定入即可)庵佣。
  android系統(tǒng)會(huì)為每個(gè)程序運(yùn)行時(shí)創(chuàng)建一個(gè)Application類的對(duì)象且僅創(chuàng)建一個(gè),所以Application可以說是單例 (singleton)模式的一個(gè)類.且application對(duì)象的生命周期是整個(gè)程序中最長(zhǎng)的汛兜,它的生命周期就等于這個(gè)程序的生命周期巴粪。因?yàn)樗侨?的單例的,所以在不同的Activity,Service中獲得的對(duì)象都是同一個(gè)對(duì)象粥谬。所以通過Application來進(jìn)行一些肛根,數(shù)據(jù)傳遞,數(shù)據(jù)共享 等,數(shù)據(jù)緩存等操作漏策。

2派哲、巧妙運(yùn)用單例模式特點(diǎn),通過Application來傳遞數(shù)據(jù)

假如有一個(gè)Activity A, 跳轉(zhuǎn)到 Activity B ,并需要推薦一些數(shù)據(jù)哟玷,通常的作法是Intent.putExtra() 讓Intent攜帶狮辽,或者有一個(gè)Bundle把信息加入Bundle讓Intent推薦Bundle對(duì)象,實(shí)現(xiàn)傳遞巢寡。但這樣作有一個(gè)問題在 于喉脖,Intent和Bundle所能攜帶的數(shù)據(jù)類型都是一些基本的數(shù)據(jù)類型,如果想實(shí)現(xiàn)復(fù)雜的數(shù)據(jù)傳遞就比較麻煩了抑月,通常需要實(shí)現(xiàn) Serializable或者Parcellable接口树叽。這其實(shí)是Android的一種IPC數(shù)據(jù)傳遞的方法。如果我們的兩個(gè)Activity在同一個(gè) 進(jìn)程當(dāng)中為什么還要這么麻煩呢谦絮,只要把需要傳遞的對(duì)象的引用傳遞過去就可以了题诵。
基本思路是這樣的。在Application中創(chuàng)建一個(gè)HashMap 层皱,以字符串為索引性锭,Object為value這樣我們的HashMap就可以存儲(chǔ)任何類型的對(duì)象了。在Activity A中把需要傳遞的對(duì)象放入這個(gè)HashMap叫胖,然后通過Intent或者其它途經(jīng)再把這索引的字符串傳遞給Activity B ,Activity B 就可以根據(jù)這個(gè)字符串在HashMap中取出這個(gè)對(duì)象了草冈。只要再向下轉(zhuǎn)個(gè)型 ,就實(shí)現(xiàn)了對(duì)象的傳遞瓮增。

六怎棱、總結(jié)

經(jīng)過網(wǎng)上的爬文終于了解了什么是單例模式,在這里感謝
http://www.cnblogs.com/java-my-life/archive/2012/03/31/2425631.html
http://blog.csdn.net/songylwq/article/details/6058771
http://www.blogjava.net/kenzhh/archive/2013/03/15/357824.html
http://www.cnblogs.com/hxsyl/archive/2013/03/19/2969489.html
http://blog.csdn.net/pi9nc/article/details/11200969

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绷跑,一起剝皮案震驚了整個(gè)濱河市互艾,隨后出現(xiàn)的幾起案子毛雇,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寻行,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門鸳谜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人式廷,你說我怎么就攤上這事“磐欤” “怎么了滑废?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)袜爪。 經(jīng)常有香客問我蠕趁,道長(zhǎng),這世上最難降的妖魔是什么辛馆? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任俺陋,我火速辦了婚禮,結(jié)果婚禮上昙篙,老公的妹妹穿的比我還像新娘腊状。我一直安慰自己,他們只是感情好苔可,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布缴挖。 她就那樣靜靜地躺著,像睡著了一般焚辅。 火紅的嫁衣襯著肌膚如雪映屋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天同蜻,我揣著相機(jī)與錄音棚点,去河邊找鬼。 笑死湾蔓,一個(gè)胖子當(dāng)著我的面吹牛瘫析,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播卵蛉,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼颁股,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了傻丝?” 一聲冷哼從身側(cè)響起甘有,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎葡缰,沒想到半個(gè)月后亏掀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忱反,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年滤愕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了温算。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡间影,死狀恐怖注竿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情魂贬,我是刑警寧澤巩割,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站付燥,受9級(jí)特大地震影響宣谈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜键科,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一闻丑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧勋颖,春花似錦嗦嗡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至咱枉,卻和暖如春卑硫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚕断。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工欢伏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亿乳。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓硝拧,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親葛假。 傳聞我的和親對(duì)象是個(gè)殘疾皇子障陶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)聊训,斷路器抱究,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139
  • 單例模式(SingletonPattern)一般被認(rèn)為是最簡(jiǎn)單、最易理解的設(shè)計(jì)模式带斑,也因?yàn)樗暮?jiǎn)潔易懂鼓寺,是項(xiàng)目中最...
    成熱了閱讀 4,227評(píng)論 4 34
  • 前言 本文主要參考 那些年勋拟,我們一起寫過的“單例模式”。 何為單例模式妈候? 顧名思義敢靡,單例模式就是保證一個(gè)類僅有一個(gè)...
    tandeneck閱讀 2,485評(píng)論 1 8
  • 一.什么是單例模式 單例模式的定義:確保一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)訪問他的全局訪問點(diǎn)苦银。單例模式是幾個(gè)設(shè)計(jì)模式中...
    Geeks_Liu閱讀 2,216評(píng)論 0 10
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法啸胧,類相關(guān)的語法,內(nèi)部類的語法幔虏,繼承相關(guān)的語法吓揪,異常的語法,線程的語...
    子非魚_t_閱讀 31,587評(píng)論 18 399