一傀履、前言
單例模式,一直以來是我們在日常開發(fā)中最常用的一種設計模式莉炉,更是面試中非常重要钓账,也非常容易被問到的問題。在日常開發(fā)中絮宁,大家常用的語言還是Java梆暮,但今天我給大家?guī)淼氖窃贙otlin語言中,單例模式是怎么編寫的绍昂,并且會對比Java方式啦粹,并說明每種方式的優(yōu)缺點。
下面會列舉5種最為常見的單例模式做對比:
- 1.餓漢式
- 2.懶漢式
- 3.同步鎖式
- 4.雙重檢測式
- 5.內部類式
二窘游、Java與Kotlin對比
1卖陵、餓漢式
餓漢式可以說是我們最先接觸單例模式的例子了,是最基本的單例寫法张峰,也是最快最懶的方式。
優(yōu)點 | 缺點 |
---|---|
簡單好寫 | 類加載就初始化了對象棒旗,影響應用啟動速度 |
下面直接看代碼吧:
Java方式
public class BaseSingleton {
//第一步:私有構造
private BaseSingleton() {
}
//第二步:私有靜態(tài)對象
private static BaseSingleton INSTANCE = new BaseSingleton();
//第三步:提供對外獲取方法
public static BaseSingleton getInstance() {
return INSTANCE;
}
}
Kotlin方式
object BaseSingleton {
}
有童鞋要說了喘批,這什么都沒寫呀撩荣。對,餓漢式在Kotlin中饶深,只需要一個object修飾符就行了餐曹,這就是Kotlin非常厲害的地方。
2.懶漢式
針對餓漢式的缺點敌厘,于是懶漢式就出現(xiàn)了台猴,因為比較簡單,下面直接分析俱两。
優(yōu)點 | 缺點 |
---|---|
只有第一次使用時饱狂,才會初始化對象 | 線程非安全,多線程中可能會出現(xiàn)創(chuàng)建多個對象 |
Java方式
public class LazyLoadSingleton {
//第一步:私有構造
private LazyLoadSingleton() {
}
//第二步:創(chuàng)建私有構造對象
private static LazyLoadSingleton INSTANCE;
//第二步:提供對外獲取方法
public static LazyLoadSingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new LazyLoadSingleton();
}
return INSTANCE;
}
}
其實就是增加了一個空判斷宪彩。
Kotlin方式
class LazyLoadSingleton private constructor(){
companion object {
//原生寫法
//LazyThreadSafetyMode.NONE 線程不安全
val INSTANCE_1 by lazy(LazyThreadSafetyMode.NONE) {
LazyLoadSingleton()
}
//翻譯JAVA的寫法
private var INSTANCE_2: LazyLoadSingleton? = null
fun getInstance(): LazyLoadSingleton {
if (INSTANCE_2 == null) {
INSTANCE_2 = LazyLoadSingleton()
}
return INSTANCE_2!!
}
}
}
Kotlin這里有兩種寫法休讳,一種是純種,一種是變種尿孔。變種大家一看就明白俊柔,就是直接把Java的方式翻譯過來了。純種的我們使用了lazy活合,看英文就知道是懶加載的方式雏婶,傳入了一個LazyThreadSafetyMode.NONE,英文好的小伙伴一看就明白白指,這是線程不安全的意思留晚。companion object的意思相當于Java中public static。
3.同步鎖式
因為懶漢式的出現(xiàn)侵续,雖然解決了餓漢式的不足倔丈,但也出現(xiàn)了多線程的問題。于是解決懶漢式的方式就出現(xiàn)了状蜗,那就是我們熟知的加鎖Synchronized需五。
優(yōu)點 | 缺點 |
---|---|
保證線程安全 | 每次都要加鎖,獲取的時候不經(jīng)濟 |
Java方式
public class LazySynchronizedSingleton {
//第一步:私有構造
private LazySynchronizedSingleton() {
}
//第二步:創(chuàng)建私有對象
private static LazySynchronizedSingleton INSTANCE;
//第三步:提供對外訪問方法
public static synchronized LazySynchronizedSingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new LazySynchronizedSingleton();
}
return INSTANCE;
}
}
只是加了一個關鍵字synchronized轧坎,沒有難理解的地方宏邮。
Kotlin方式
class LazySynchronizedSingleton private constructor(){
companion object {
private var INSTANCE: LazySynchronizedSingleton? = null
//在Kotlin中,我們就用的注釋方式來加鎖
@Synchronized
fun getInstance(): LazySynchronizedSingleton {
if (INSTANCE == null) {
INSTANCE = LazySynchronizedSingleton()
}
return INSTANCE!!
}
}
}
在Kotlin中缸血,我們要使用的是注解方式@Synchronized蜜氨,就能達到同步鎖的目的了。
4.雙重檢測式
第3種方式應該已經(jīng)滿足日常大部分的需求捎泻,但對我們程序員來說飒炎,不斷的優(yōu)化才是學習之道。那么針對每次獲取都會加鎖的問題笆豁,要怎么解決呢郎汪?雙重檢測式就出現(xiàn)了赤赊。
優(yōu)點 | 缺點 |
---|---|
第一次獲取的時候才會加鎖 | 寫法有點難,考點記性 |
Java方式
public class DoubleCheckSingleton {
//第一步:私有構造
private DoubleCheckSingleton() {
}
//第二步:創(chuàng)建靜態(tài)對象
private static volatile DoubleCheckSingleton INSTANCE;
//第三步:對外公開獲取方法
public static DoubleCheckSingleton getInstance() {
if (INSTANCE == null) {
synchronized (DoubleCheckSingleton.class) {
if (INSTANCE == null) {
INSTANCE = new DoubleCheckSingleton();
}
}
}
return INSTANCE;
}
}
細心的童鞋發(fā)現(xiàn)煞赢,我們第二步用到了volatile抛计,關于volatile不是本文的重點,所以這里不展開說明照筑。在第三步中吹截,我們首先判斷一次空,如果是空凝危,就加鎖波俄,然后再判斷一次空,如果為空就創(chuàng)建媒抠。這樣的好處就是上面優(yōu)點說到的弟断,只會鎖一次。缺點大家也發(fā)現(xiàn)了趴生,不僅要必須寫volatile阀趴,方法中的步驟也不能錯。
Kotlin方式
class DoubleCheckSingleton private constructor() {
companion object {
//Kotlin原生寫法
val INSTANCE_1 by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
DoubleCheckSingleton()
}
//翻譯JAVA代碼寫法
@Volatile
private var INSTANCE_2: DoubleCheckSingleton? = null
fun getInstance(): DoubleCheckSingleton {
if (INSTANCE_2 == null) {
synchronized(DoubleCheckSingleton::class) {
if (INSTANCE_2 == null) {
INSTANCE_2 = DoubleCheckSingleton()
}
}
}
return INSTANCE_2!!
}
}
}
又來兩種寫法苍匆,變種的我就不解釋了刘急。純種的,我們只改變了lazy的括號的值浸踩,mode = LazyThreadSafetyMode.SYNCHRONIZED就是鎖的意思叔汁,英文好的童鞋一眼就明白了。
5.內部類式
最后一種方式检碗,不僅滿足了懶加載据块、線程安全,代碼也非常少折剃,是非常推薦的一種方式另假。
優(yōu)點 | 缺點 |
---|---|
簡單,代碼少 | 暫無 |
Java方式
public class InnerStaticSingleton {
private InnerStaticSingleton() {
}
private static class Holder {
private static InnerStaticSingleton INSTANCE = new InnerStaticSingleton();
}
public static InnerStaticSingleton getInstance() {
return Holder.INSTANCE;
}
內部類Holder怕犁,里面有外部的實例边篮。很多童鞋可能要問,這怎么就滿足懶漢式和線程安全呢奏甫?當我們應用初始化時戈轿,getInstance沒有被調用,就沒有實例對象阵子,那就滿足了懶漢式思杯。當我們調用getInstance的時候,Java虛擬機為了保證類加載的安全性挠进,所以這里就保證了線程安全智蝠。這種寫法是不是驚呆了腾么?那Kotlin又是怎么樣寫的呢?
Kotlin方式
class InnerStaticSingleton private constructor() {
companion object {
fun getInstance() = Holder.INSTANCE
}
private object Holder {
val INSTANCE = InnerStaticSingleton()
}
}
很簡單杈湾,內部用object創(chuàng)建一個內部Holder單例,外部一個getInstance來獲取的方法攘须。也相當于是Java翻譯過來的方式漆撞。
三、總結
為了方便大家于宙,我創(chuàng)建了一個項目浮驳,專門供大家對比學習。
這是Github項目捞魁,里面有詳細的代碼注釋至会。如果對你有幫助,歡迎大家star谱俭,謝謝奉件!
https://github.com/FynnJason/FiveSingletonDemo
如果本文對你有幫助,歡迎點贊昆著,贊賞請我喝杯Java...
如果有什么疑問县貌,下方評論留言...