作者已經(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)注留言投幣丟香蕉。
什么是單例模式
- 單例模式是設(shè)計(jì)模式中最簡(jiǎn)單的形式之一。
- 一個(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,年輕小伙竟作出這種事!