我們先看單例模式的定義:?jiǎn)卫J皆贘ava中是最簡(jiǎn)單的一種設(shè)計(jì)模式野哭,一個(gè)類負(fù)責(zé)創(chuàng)建自身唯一的對(duì)象,該對(duì)象可以全局訪問(wèn)幻件,且外部不需要實(shí)例化拨黔。
有些朋友剛剛從Java切到Kotlin,語(yǔ)法上有些不習(xí)慣绰沥,創(chuàng)建單例的時(shí)候不知道怎么寫(xiě)篱蝇,下面我提供幾種方法。
一徽曲、餓漢式
Java:
public class SingletonObject{
private static SingletonObject instance = new SingletonObject();
private SingleObject(){}
public static SingletonObject getInstance(){
return instance;
}
public void something(){
//do something
}
}
Kotlin:
object SingletonObject{
fun something(){
//do something
}
}
一行代碼就可以實(shí)現(xiàn)零截,通過(guò)反編譯成java語(yǔ)言不難發(fā)現(xiàn)寫(xiě)法上都是一樣的
public final class SingletonObject {
@NotNull
public static final SingletonObject INSTANCE;
public final void something() {
}
private SingletonObject() {
}
static {
SingletonObject var0 = new SingletonObject();
INSTANCE = var0;
}
}
優(yōu)點(diǎn):最簡(jiǎn)單的一種方式,在類加載時(shí)就實(shí)例化秃臣,所以它是線程安全的涧衙,因?yàn)闆](méi)有鎖,不會(huì)影響訪問(wèn)效率奥此。
缺點(diǎn):在類加載時(shí)不管這個(gè)單例中的實(shí)例有沒(méi)有用到都被實(shí)例化了弧哎,在未被使用時(shí)會(huì)浪費(fèi)內(nèi)存。
總結(jié):類加載的原因雖然有很多稚虎,但單例類往往是因?yàn)檎{(diào)用了內(nèi)部的靜態(tài)函數(shù)撤嫩,所以一般情況下這種寫(xiě)法也能做到懶加載。我個(gè)人非常推薦這種寫(xiě)法蠢终,而且既然語(yǔ)言官方支持序攘,說(shuō)明這是在內(nèi)存和效率權(quán)衡下的最優(yōu)方案。
二寻拂、懶漢式
Java:
public class SingletonObject {
private static SingletonObject instance;
private SingletonObject() {
}
public static SingletonObject getInstance() {
if (instance == null) {
instance = new SingletonObject();
}
return instance;
}
public void something() {
//do something
}
}
Kotlin:
class SingletonObject private constructor(){
fun something() {
//do something
}
companion object {
var instance: SingletonObject? = null
get() {
if (field == null) {
field = SingletonObject()
}
return field
}
}
}
優(yōu)點(diǎn):僅僅在使用時(shí)才會(huì)實(shí)例化這個(gè)單例程奠,減少了內(nèi)存占用。
缺點(diǎn):線程不安全兜喻,并且Kotlin的空安全特性會(huì)讓你用起來(lái)非常難受梦染,Koltin中每次訪問(wèn)這個(gè)單例都要處理空安全赡麦。
總結(jié):如果能確保沒(méi)有多線程訪問(wèn)朴皆,這種方法未嘗不可帕识。
三、加鎖懶漢式
總結(jié)了第二種方式的缺點(diǎn)遂铡,很容易想到可以在訪問(wèn)時(shí)加鎖解決問(wèn)題肮疗,加鎖懶漢式應(yīng)運(yùn)而生。
Java:
public class SingletonObject {
private static SingletonObject instance;
private SingletonObject() {
}
public static synchronized SingletonObject getInstance() {
if (instance == null) {
instance = new SingletonObject();
}
return instance;
}
public void something() {
//do something
}
}
Koltin:
class SingletonObject private constructor() {
fun something() {
//do something
}
companion object {
var instance: SingletonObject? = null
@Synchronized get() {
if (field == null) {
field = SingletonObject()
}
return field
}
}
}
優(yōu)點(diǎn):僅僅在使用時(shí)才會(huì)實(shí)例化這個(gè)單例扒接,減少了內(nèi)存占用伪货,同時(shí)兼顧線程安全。
缺點(diǎn):每次訪問(wèn)這個(gè)單例都要同步鎖(效率低)钾怔、Koltin中要處理空安全碱呼。
總結(jié):如果獲取實(shí)例的次數(shù)很少,這種方法未嘗不可宗侦。
四愚臀、雙檢鎖懶漢式
既然知道了前兩種方案的缺點(diǎn),那有沒(méi)有完美方案呢矾利?有姑裂,雙檢鎖懶漢式就是
Java:
public class SingletonObject {
private static SingletonObject instance;
private SingletonObject() {
}
public static SingletonObject getInstance() {
if (instance == null) {
synchronized (SingletonObject.class) {
if (instance == null) {
instance = new SingletonObject();
}
}
}
return instance;
}
public void something() {
//do something
}
}
Kotlin:
class SingletonObject private constructor() {
fun something() {
//do something
}
companion object {
var instance: SingletonObject? = null
get() {
if (field == null) {
synchronized(SingletonObject::class.java) {
if (field == null) {
field = SingletonObject()
}
}
}
return field
}
}
}
優(yōu)點(diǎn):僅僅在使用時(shí)才會(huì)實(shí)例化這個(gè)單例,減少了內(nèi)存占用男旗,同時(shí)兼顧線程安全舶斧。
缺點(diǎn):寫(xiě)法較繁瑣、Koltin中要處理空安全察皇。
總結(jié):比較完美的方案茴厉,兼顧了效率和內(nèi)存,如果在Kotlin中什荣,那這不是最好的方案呀忧。
五、靜態(tài)內(nèi)部類
Java:
public class SingletonObject {
private static class LazyHolder{
private static SingletonObject instance = new SingletonObject();
}
private SingletonObject() {
}
public static SingletonObject getInstance() {
return LazyHolder.instance;
}
public void something() {
//do something
}
}
Koltin:
class SingletonObject private constructor() {
fun something() {
//do something
}
val instance: SingletonObject
get() = LazyHolder.singletonObject
private object LazyHolder {
val singletonObject = SingletonObject()
}
}
對(duì)于這種實(shí)現(xiàn)方式溃睹,其實(shí)Kotlin還有另一種寫(xiě)法而账,通過(guò)lazy委托,寫(xiě)起來(lái)要簡(jiǎn)潔不少
class SingletonObject private constructor() {
fun something() {
//do something
}
val instance: SingletonObject by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
SingletonObject()
}
}
優(yōu)點(diǎn):僅僅在使用時(shí)才會(huì)實(shí)例化這個(gè)單例因篇,減少了內(nèi)存占用泞辐,同時(shí)兼顧線程安全。
總結(jié):算是第一種方法的補(bǔ)充版竞滓,通過(guò)可控類加載保證懶加載效果咐吼,沒(méi)有明顯的短板。不推薦的原因是會(huì)多寫(xiě)很多代碼商佑,Koltin調(diào)用時(shí)也不如第一種方式簡(jiǎn)單锯茄。
六、枚舉
Java:
enum SingletonObject {
INSTANCE;
public void something() {
//do something
}
}
Kotlin:
enum class SingletonObject {
INSTANCE;
fun something() {
//do something
}
}
優(yōu)點(diǎn):簡(jiǎn)單、線程安全肌幽,通過(guò)枚舉可以有效防止重復(fù)實(shí)例化晚碾,據(jù)說(shuō)還能防止反射。
總結(jié):比較冷門喂急,沒(méi)有明顯短板格嘁。