Java與Kotlin的單例模式(霸氣.jpg)

作者已經(jīng)搬遷去隔壁網(wǎng)站,也歡迎大家關(guān)注我們的寫(xiě)作團(tuán)隊(duì):天星技術(shù)團(tuán)隊(duì)

題外話

上一次被人說(shuō)文章名字取得不霸氣,于是這一次我采用了這么霸氣的名字,但實(shí)際上我是一個(gè)很低調(diào)的人。設(shè)計(jì)模式剛?cè)腴T(mén)的小伙伴可以先看看這篇《設(shè)計(jì)模式入門(mén)》,在文章末尾也將列出“設(shè)計(jì)模式系列”文章。歡迎大家關(guān)注留言投幣丟香蕉。

什么是單例模式

  1. 單例模式是設(shè)計(jì)模式中最簡(jiǎn)單的形式之一。
  2. 一個(gè)類有且僅有一個(gè)對(duì)象實(shí)例,并自行實(shí)例化向整個(gè)系統(tǒng)提供。

為何要學(xué)習(xí)單例模式

1. 有些對(duì)象我們只需要一個(gè)。例如:線程池,緩存,對(duì)話框等等,創(chuàng)建太多此類實(shí)例可能會(huì)導(dǎo)致程序行為異常、資源使用過(guò)量等問(wèn)題。
2. 防止造成資源浪費(fèi)。靜態(tài)變量也可以給整個(gè)系統(tǒng)提供一個(gè)實(shí)例,但此對(duì)象在程序一開(kāi)始就被創(chuàng)建好了,萬(wàn)一在某次運(yùn)行中沒(méi)有使用到此對(duì)象,資源就被浪費(fèi)了。
3. 沒(méi)有富蘿莉包養(yǎng)我。

走進(jìn)單例模式

單例模式有三個(gè)要點(diǎn),分別是:
1. 僅有一個(gè)實(shí)例。
2. 自動(dòng)實(shí)例化。
3. 向整個(gè)系統(tǒng)提供。
這也就是我們?cè)趯?xiě)單例模式時(shí)候必須要遵守的幾點(diǎn)。根據(jù)上面的條件,我們能夠得出:
1. 構(gòu)造方法必須私有化,禁止外部隨意new Singleton();
2. 類內(nèi)部實(shí)例化一個(gè)實(shí)例對(duì)象。
3. 對(duì)外提供一個(gè)可以獲取內(nèi)部唯一實(shí)例對(duì)象的方法。

懶漢式

懶漢式這名字的精髓在于懶,在代碼中,這種懶代表著需要的時(shí)候才創(chuàng)建實(shí)例,不需要就不創(chuàng)建了,這樣保證了資源的不浪費(fèi)。

Java懶漢式

public class JavaSingleton {

    private JavaSingleton(){}//私有構(gòu)造方法
    private static JavaSingleton instance;//提供一個(gè)實(shí)例對(duì)象

     public static JavaSingleton getInstance(){//對(duì)外提供可獲取唯一實(shí)例化對(duì)象的方法
        if(instance == null)
            instance = new JavaSingleton(); //延遲實(shí)例化
        return instance;
    }
}

Kotlin懶漢式1

class KotlinSingleton private constructor() {
    private var instance : KotlinSingleton? = null

    fun getInstance(): KotlinSingleton? {
        if(instance == null)
            instance = KotlinSingleton()
        return instance
    }
}

Kotlin懶漢式2

class KotlinSingleton private constructor() {
    companion object {
        val instance by lazy(LazyThreadSafetyMode.NONE) {
            KotlinSingleton()
        }
    }
}

// 在Kotlin 中調(diào)用
KotlinSingleton.instance.xx()
// 在Java 中調(diào)用
KotlinSingleton.Companion.getInstance().xx()

kotlin方式1就是直譯了java方式,沒(méi)啥說(shuō)的。kotlin方式2寫(xiě)著很簡(jiǎn)潔,也很明了。要提一點(diǎn)的就是companion object,他類似public static,這一點(diǎn)從下面調(diào)用方式也看得出來(lái)。lazy屬性表明是懶加載方式,LazyThreadSafetyMode.NONE表明了這種方式,線程不安全。
為啥不安全????!
我們已經(jīng)按照單例模式三個(gè)要點(diǎn)寫(xiě)出了單例模式,其實(shí)都已經(jīng)講完了。但為何還有其他一些寫(xiě)法呢? 因?yàn)槲覀兪浅绦騿T??!優(yōu)化!優(yōu)化!優(yōu)化!
單線程進(jìn)程下,懶漢式完全夠用。但實(shí)際開(kāi)發(fā)哪還有什么單線程程序。既然存在多線程,就存在并發(fā)性,如果兩個(gè)線程同時(shí)進(jìn)入非空判斷,問(wèn)題就出現(xiàn)了。線程一可能創(chuàng)建了一個(gè)instance,線程二因?yàn)橐呀?jīng)非空判斷為空了,所以也創(chuàng)建了個(gè)instance。這樣程序肯定不會(huì)朝著你預(yù)想的方向去。所以我們可以給非空判斷加一把鎖,防止多線程同時(shí)進(jìn)入非空判斷。

同步鎖

Java方式

public class JavaSingleton {

    private JavaSingleton(){}//私有構(gòu)造方法
    private static JavaSingleton instance;//提供一個(gè)實(shí)例對(duì)象

     public static synchronized JavaSingleton getInstance(){//對(duì)外提供可獲取唯一實(shí)例化對(duì)象的方法
        if(instance == null)
            instance = new JavaSingleton(); //延遲實(shí)例化
        return instance;
    }
}

Kotlin方式

class KotlinSingleton private constructor() {
    private var instance : KotlinSingleton? = null

    @Synchronized //用注解就okk了。
    fun getInstance(): KotlinSingleton? {
        if(instance == null)
            instance = KotlinSingleton()
        return instance
    }
}

簡(jiǎn)單!粗暴!
太粗暴了!Synchronized 是一把重型鎖,對(duì)性能影響相當(dāng)?shù)拇?,像單例這樣在軟件中可能多次獲取,那性能將會(huì)大大地降低!所以,這種方式不建議使用。

餓漢式

為了解決多線程的問(wèn)題,又不想降低性能,“餓漢式”就來(lái)了。餓漢式名字精髓在于“餓”,迫切的想吃東西,在代碼中表示,迫切的想有一個(gè)單例對(duì)象!即在初始化的時(shí)候,就把單例創(chuàng)建好,之后要用就直接return,但是不用就浪費(fèi)了。這也是餓漢式的缺點(diǎn)。
Java方式

public class JavaSingleton {

    private JavaSingleton(){}//私有構(gòu)造方法
    private static JavaSingleton instance = new JavaSingleton();//提供一個(gè)實(shí)例對(duì)象

    public static JavaSingleton getInstance(){//對(duì)外提供可獲取唯一實(shí)例化對(duì)象的方法
        return instance;
    }
}

Kotlin方式

object KotlinSingleton {
    //null
}

// 在Kotlin 中調(diào)用
KotlinSingleton.xx()
// 在Java 中調(diào)用
KotlinSingleton.INSTANCE.xx()

酥糊!Kotlin!有牌面!

雙重檢查鎖

前面講的三種方式都有不同程度的缺點(diǎn)。而雙重檢查鎖,既保證了延遲加載不浪費(fèi)資源,又保證了較好的性能,不采用重型鎖。但雙重檢查鎖不適用于1.4以及更早版本的java。

Java方式

public class JavaSingleton {

    private JavaSingleton(){}
    //volatile確保了當(dāng)instance初始化為JavaSingleton實(shí)例時(shí),多個(gè)線程正確的處理instance變量。
    private volatile static JavaSingleton instance;

   public static JavaSingleton getInstance(){
       if(instance == null){
           synchronized (JavaSingleton.class){//只有第一次才徹底執(zhí)行鎖塊代碼
               if(instance == null){
                   instance = new JavaSingleton();
               }
           }
       }
       return instance;
   }
}

Kotlin方式1

class KotlinSingleton private constructor() {
    @Volatile  //用注解就okk了
    private var instance : KotlinSingleton? = null

    fun getInstance(): KotlinSingleton? {
        if(instance == null){
            synchronized(KotlinSingleton::class){
                if(instance == null){
                    instance = KotlinSingleton()
                }
            }
        }
        return instance
    }
}

Kotlin方式2

class KotlinSingleton private constructor() {
    companion object {
        val instance by lazy (LazyThreadSafetyMode.SYNCHRONIZED){
            KotlinSingleton()
        }
    }
}

// 在Kotlin 中調(diào)用
KotlinSingleton.instance.xx()
// 在Java 中調(diào)用
KotlinSingleton.Companion.getInstance().xx()

“雙重檢查鎖”的沒(méi)有明顯的缺點(diǎn),如果非要說(shuō)一個(gè),可能就是太復(fù)雜了。kotlin還好,java里面寫(xiě)著相當(dāng)?shù)膹?fù)雜。如果程序?qū)π阅軟](méi)有考慮的話,這樣寫(xiě)顯然就太麻煩了。

關(guān)于LazyThreadSafetyMode
延遲屬性 Lazy 可選LazyThreadSafetyMode三種模式:

  • SYNCHRONIZED —— 雙重檢查鎖式,默認(rèn)使用。
  • PUBLICATION —— 允許多個(gè)線程同時(shí)初始化實(shí)例,但只采用最先返回的實(shí)例。
  • NONE —— 沒(méi)有任何的線程安全的保證和開(kāi)銷。

內(nèi)部類式(店長(zhǎng)推薦?。。?/h2>

不要私信問(wèn)我店長(zhǎng)是誰(shuí)!
總之這種方式,解決了延遲加載,線程安全的問(wèn)題,還代碼量少!簡(jiǎn)直美滋滋!但跟之前不同的是,沒(méi)有聲明實(shí)例對(duì)象。
Java方式

public class JavaSingleton {

    private JavaSingleton(){}

    private static class Holder{
        private static JavaSingleton instance = new JavaSingleton();
    }

    public static JavaSingleton getInstance(){
        return Holder.instance;
    }
}

Kotlin方式

class KotlinSingleton private constructor() {
    companion object {
        fun getInstance() = Holder.instance
    }

    private object Holder{
        val instance = KotlinSingleton()
    }
}

在類加載時(shí),因?yàn)闆](méi)有調(diào)用getInstance()所以Holder也不會(huì)加載,這樣就實(shí)現(xiàn)了懶加載。調(diào)用getInstance()時(shí),JVM會(huì)主動(dòng)保證類加載的安全性,所以線程也是安全的。kotlin的寫(xiě)法就是java的翻譯版本。

民間大神版本

上面幾種方式都是比較官方的版本,下面介紹幾個(gè)民間版本,是真大神還是抖機(jī)靈,自行判斷。我也去研究研究!


來(lái)自知乎@丌冰:
“我覺(jué)得這樣就好了,什么懶加載,雙重什么什么,什么什么的”

fun main(args: Array<String>) {
    Instance.INSTANCE.fun1()
    print(Instance.INSTANCE.fun2())
}
enum class Instance {
    INSTANCE;

    fun fun1(){
    }
    fun fun2():Any?{
        return null
    }
}

作者:丌冰
鏈接:https://www.zhihu.com/question/52377186/answer/303561470
來(lái)源:知乎
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

可見(jiàn)Android官方并不推薦使用枚舉,占用內(nèi)存較多。

來(lái)自劉望舒設(shè)計(jì)模式(二)單例模式的七種寫(xiě)法

public class SingletonManager { 
  private static Map<String, Object> objMap = new HashMap<String,Object>();
  private Singleton() { 
  }
  public static void registerService(String key, Objectinstance) {
    if (!objMap.containsKey(key) ) {
      objMap.put(key, instance) ;
    }
  }
  public static ObjectgetService(String key) {
    return objMap.get(key) ;
  }
}

這也是比較少見(jiàn)的單例寫(xiě)法,將多個(gè)單例放在進(jìn)SingletonManager 的靜態(tài)map中統(tǒng)一管理。我是覺(jué)得有點(diǎn)太過(guò)于復(fù)雜,容易出錯(cuò)。

以上版本僅供參考,要是用了對(duì)程序造成了什么不好的影響,別找我。

關(guān)于單例的其他問(wèn)題

關(guān)于繼承: 不能繼承!別想了。
關(guān)于單例: 盡量少使用單例。
關(guān)于我還沒(méi)想到的問(wèn)題: 歡迎加群討論557247785。

總結(jié)

單例方式 優(yōu)點(diǎn) 缺點(diǎn)
懶漢式 懶加載 線程不安全
同步鎖 線程安全 多次獲取的性能很低
餓漢式 簡(jiǎn)單、易寫(xiě) 可能引起資源浪費(fèi)
雙重檢查鎖 第一次獲取才加鎖 寫(xiě)法復(fù)雜
內(nèi)部類式 簡(jiǎn)單、易寫(xiě) 暫無(wú)
民間大神式 歡迎留言跟我討論 或者加qq群:557247785

以下是我“設(shè)計(jì)模式系列”文章,歡迎大家關(guān)注留言投幣丟香蕉。
也可以進(jìn)群跟大神們討論。qq群:557247785

設(shè)計(jì)模式入門(mén)
Java與Kotlin的單例模式
Kotlin的裝飾者模式與源碼擴(kuò)展
由淺到深了解工廠模式
為了學(xué)習(xí)Rxjava,年輕小伙竟作出這種事!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 單例模式(SingletonPattern)一般被認(rèn)為是最簡(jiǎn)單、最易理解的設(shè)計(jì)模式,也因?yàn)樗暮?jiǎn)潔易懂,是項(xiàng)目中最...
    成熱了閱讀 4,549評(píng)論 4 34
  • 在一個(gè)方法內(nèi)部定義的變量都存儲(chǔ)在棧中,當(dāng)這個(gè)函數(shù)運(yùn)行結(jié)束后,其對(duì)應(yīng)的棧就會(huì)被回收,此時(shí),在其方法體中定義的變量將不...
    Y了個(gè)J閱讀 4,585評(píng)論 1 14
  • 1.單例模式概述 (1)引言 單例模式是應(yīng)用最廣的模式之一,也是23種設(shè)計(jì)模式中最基本的一個(gè)。本文旨在總結(jié)通過(guò)Ja...
    曹豐斌閱讀 3,078評(píng)論 6 47
  • 這貨書(shū)名夠風(fēng)騷,內(nèi)容夠羅嗦,逼格略低,這是多大仇啊。不過(guò)內(nèi)容夠詳細(xì),邏輯完整的讓人抓狂,淺顯易懂。還有盡是泛泛之談...
    拉斯閱讀 541評(píng)論 0 0

友情鏈接更多精彩內(nèi)容