以最簡(jiǎn)單的module學(xué)習(xí)設(shè)計(jì)模式,理解最重要
設(shè)計(jì)模式持續(xù)更新中:http://www.itdecent.cn/p/e3c25095c31f
連著更了幾天,今天寫(xiě)個(gè)簡(jiǎn)單的,單例模式
前言
單例模式(Singleton Pattern)是 Java 中最簡(jiǎn)單的設(shè)計(jì)模式之一。
這種模式涉及到一個(gè)單一的類(lèi),該類(lèi)負(fù)責(zé)創(chuàng)建自己的對(duì)象,同時(shí)確保只有單個(gè)對(duì)象被創(chuàng)建。這個(gè)類(lèi)提供了一種訪問(wèn)其唯一的對(duì)象的方式,可以直接訪問(wèn),不需要實(shí)例化該類(lèi)的對(duì)象。
注意:
1、單例類(lèi)只能有一個(gè)實(shí)例。
2、單例類(lèi)必須自己創(chuàng)建自己的唯一實(shí)例。
3、單例類(lèi)必須給所有其他對(duì)象提供這一實(shí)例。
正文:
注:由于單例模式是經(jīng)常用到的,這里就不提供demo了(懶。。。)
本文就總結(jié)了Java中單例模式的幾種實(shí)現(xiàn)方式,并比較了它們的優(yōu)缺點(diǎn)
1. 最簡(jiǎn)單的實(shí)現(xiàn)---餓漢式
Java
/**
* 餓漢式
*/
public class Single {
private static Single single = new Single();
private Single() {
}
public static Single getInstance() {
return single;
}
}
寫(xiě)一個(gè)單例(不管什么形式),主要注意點(diǎn)如下幾點(diǎn)(該種方式不存在線程安全的問(wèn)題,其是線程安全的)
- 成員變量 single 要聲明成靜態(tài)的(static),因?yàn)樾枰陟o態(tài)方法getInstance()中訪問(wèn);
- 構(gòu)造方法要聲明成私有,不然如何保證單例;
- getInstance()要聲明成 public static的。
Kotlin
/**
* 餓漢式
*/
object Single {
}
有童鞋要說(shuō)了,這什么都沒(méi)寫(xiě)呀。對(duì),餓漢式在Kotlin中,只需要一個(gè)object修飾符就行了,這就是Kotlin非常厲害的地方。
2.性能優(yōu)化(lazy load)——懶漢式
餓漢式的方式雖然簡(jiǎn)單,但是是基于classloader加載的,其在該類(lèi)第一次加載進(jìn)內(nèi)存時(shí)就會(huì)初始化單例對(duì)象。這樣,無(wú)論該對(duì)象是否被使用,都會(huì)創(chuàng)建一個(gè)single對(duì)象。
Java
/**
* 懶漢式 --- 非線程安全
*/
public class Single {
private static Single single;
private Single() {
}
public static Single getInstance() {
if (single == null) {
single = new Single();
}
return single;
}
}
Kotlin
/**
* 懶漢式 --- 非線程安全
*/
class Single private constructor() {
companion object {
/**
* Kotlin原生寫(xiě)法
*/
val INSTANCL_1 by lazy(LazyThreadSafetyMode.NONE) {
Single()
}
/**
* 翻譯java的寫(xiě)法
*/
private var single: Single? = null
fun getInstance(): Single {
if (single == null) {
single = Single()
}
return single!!
}
}
}
Kotlin這里有兩種寫(xiě)法,一種是純種,一種是變種。變種大家一看就明白,就是直接把Java的方式翻譯過(guò)來(lái)了。純種的我們使用了lazy,看英文就知道是懶加載的方式,傳入了一個(gè)LazyThreadSafetyMode.NONE,英文好的小伙伴一看就明白,這是線程不安全的意思。companion object的意思相當(dāng)于Java中public static。
3. 懶漢式——線程安全(1)
因?yàn)閼袧h式的出現(xiàn),雖然解決了餓漢式的不足,但也出現(xiàn)了多線程的問(wèn)題。于是解決懶漢式的方式就出現(xiàn)了,那就是我們熟知的加鎖Synchronized。
Java
/**
* 懶漢式 --- 線程安全
* 使用synchronized保證線程安全
* 雖然線程安全了,但因?yàn)槭褂胹ynchronized關(guān)鍵字使加鎖效率不高
*/
public class Single {
private static Single instance;
private Single() {
}
public static synchronized Single getInstance() {
if (instance == null) {
instance = new Single();
}
return instance;
}
}
Kotlin
/**
* 懶漢式 --- 線程安全
* 使用synchronized保證線程安全
* 雖然線程安全了,但因?yàn)槭褂胹ynchronized關(guān)鍵字使加鎖效率不高
* Kotlin使用@Synchronized注解加鎖
*/
class Single private constructor() {
companion object {
private var single: Single? = null
@Synchronized
fun getInstance(): Single {
if (single == null) {
single = Single()
}
return single!!
}
}
}
4. 懶漢式——線程安全(2):效率更高
Java
/**
* 懶漢式 --- 線程安全 雙重校驗(yàn)鎖
*/
public class Single {
private static Single single;
private Single() {
}
public static Single getInstance() {
if (single == null) {
synchronized (Single.class) {
if (single == null) {
single = new Single();
}
}
}
return single;
}
}
但是上面的單例都有其缺陷:當(dāng)反序列化和使用java的反射機(jī)制時(shí),單例無(wú)法得到保證,那么,解決該問(wèn)題,我們可以使用Enum(枚舉)。
Kotlin
/**
* 懶漢式 --- 線程安全 雙重校驗(yàn)鎖
*/
class Single private constructor() {
companion object {
/**
* Kotlin原生寫(xiě)法
*/
val INSTANCE_1 by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
Single()
}
/**
* 翻譯java寫(xiě)法(變種方法)
*/
private var single: Single? = null
fun getInstance(): Single {
if (single == null) {
synchronized(Single::class.java) {
if (single == null) {
single = Single()
}
}
}
return single!!
}
}
}
Kotlin原生的,我們只改變了lazy的括號(hào)的值,mode = LazyThreadSafetyMode.SYNCHRONIZED就是鎖的意思,英文好的童鞋一眼就明白了。
5. 枚舉實(shí)現(xiàn)
這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線程同步問(wèn)題,而且還能防止反序列化和反射機(jī)制重新創(chuàng)建新的對(duì)象,不過(guò),JDK1.5中才加入enum特性, 這種方式只能在1.5之后使用。(但是Android官網(wǎng)不建議使用enums,占用內(nèi)存多(Enums often require more than twice as much memory as static constants.))
Java
/**
* 枚舉實(shí)現(xiàn)
*/
public enum Single {
INSTANCE;
public void method() {
}
}
Kotlin
/**
* 枚舉實(shí)現(xiàn)
*/
public enum Single {
INSTANCE;
public void method() {
}
}
6.內(nèi)部類(lèi)式
雖然不怎么被大家使用,但是覺(jué)得還是比較好。還是要說(shuō)一下的
Java
/**
* 內(nèi)部類(lèi)實(shí)現(xiàn)
*/
public class Single {
private Single() {
}
private static class Hodler {
private static Single single = new Single();
}
public static Single getInstance() {
return Hodler.single;
}
}
內(nèi)部類(lèi)Holder,里面有外部的實(shí)例。很多童鞋可能要問(wèn),這怎么就滿足懶漢式和線程安全呢?當(dāng)我們應(yīng)用初始化時(shí),getInstance沒(méi)有被調(diào)用,就沒(méi)有實(shí)例對(duì)象,那就滿足了懶漢式。當(dāng)我們調(diào)用getInstance的時(shí)候,Java虛擬機(jī)為了保證類(lèi)加載的安全性,所以這里就保證了線程安全。這種寫(xiě)法是不是驚呆了?那Kotlin又是怎么樣寫(xiě)的呢?
/**
* 內(nèi)部類(lèi)實(shí)現(xiàn)
*/
class Single private constructor() {
companion object {
fun getInstance(): Single {
return Hodler.single
}
}
private object Hodler {
val single = Single()
}
}
很簡(jiǎn)單,內(nèi)部用object創(chuàng)建一個(gè)內(nèi)部Holder單例,外部一個(gè)getInstance來(lái)獲取的方法。也相當(dāng)于是Java翻譯過(guò)來(lái)的方式。
如果你對(duì)此感興趣,可訂閱(感謝你的關(guān)注):
