單例,顧名思義,就是整個(gè)進(jìn)程運(yùn)行過(guò)程中只有一個(gè)實(shí)例悯嗓,單例對(duì)象的特征一般如下:
- 生命周期較長(zhǎng)件舵,通常在進(jìn)程結(jié)束前都不需要釋放;或者使用頻率很高脯厨,反復(fù)創(chuàng)建不如犧少量?jī)?nèi)存來(lái)?yè)Q取性能铅祸。
- 避免對(duì)共享資源的多重占用。
Java中實(shí)現(xiàn)單例一般有餓漢式和懶漢式兩種主要方式。其中懶漢式又有多種拓展做法临梗,如雙重檢測(cè)式涡扼、靜態(tài)內(nèi)部類式、枚舉式等盟庞。
餓漢式
指在對(duì)象沒(méi)有被使用到的時(shí)候創(chuàng)建吃沪。寫(xiě)法如下:
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
public static getInstance() {
return INSTANCE;
}
private Singleton() {
}
}
這種方式利用了類加載過(guò)程是唯一的并且安全的特性,但這種方式也有以下的限制或弊端:
- 構(gòu)造函數(shù)不能帶參數(shù)什猖,或者參數(shù)本身不需要外界上下文信息票彪。
- 可以使用反射創(chuàng)建多個(gè)對(duì)象。
- 如果對(duì)象始終沒(méi)有被使用到(類使用到了)不狮,則白白浪費(fèi)了內(nèi)存降铸。
另外這種實(shí)現(xiàn)方法還有一種變體,就是在類靜態(tài)代碼塊中實(shí)例化對(duì)象摇零。
懶漢式
public class Singleton {
private static Singleton sInstance;
public synchronized static getInstance(Context context) {
if(sInstance == null) {
sInstance = new Singleton(context);
}
return sInstance;
}
private Singleton(Context context) {
}
}
這種寫(xiě)法的優(yōu)點(diǎn)是在使用時(shí)才創(chuàng)建對(duì)象推掸,避免了內(nèi)存浪費(fèi),但也存在以下限制或弊端:
- 可以使用反射創(chuàng)建多個(gè)對(duì)象驻仅。
- 每次調(diào)用getInstance()方法都要加鎖谅畅,在高并發(fā)訪問(wèn)中會(huì)降低效率,為了改進(jìn)這個(gè)問(wèn)題雾家,便有了雙重檢測(cè)式的出現(xiàn)铃彰。
雙重檢測(cè)
public class Singleton {
private static Singleton sInstance;
public static getInstance(Context context) {
if(sInstance == null) {
synchronized(Singleton.class) {
if(sInstance == null) {
sInstance = new Singleton(context);
}
}
}
return sInstance;
}
private Singleton(Context context) {
}
}
這種實(shí)現(xiàn)方式只有當(dāng)對(duì)象未實(shí)例化前會(huì)加鎖,提高了訪問(wèn)效率芯咧,但也存在以下限制或弊端:
- 可以使用反射創(chuàng)建多個(gè)對(duì)象牙捉。
靜態(tài)內(nèi)部類
這種方法是餓漢式的優(yōu)化,采用了和餓漢式相同的原理同時(shí)又保證不會(huì)浪費(fèi)內(nèi)存敬飒,具體實(shí)現(xiàn)如下:
public class Singleton {
public static getInstance() {
return Inner.INSTANCE;
}
private Singleton() {
}
private static class Inner {
static final Singleton INSTANCE = new Singleton();
}
}
這種方式有以下的限制或弊端:
- 構(gòu)造函數(shù)不能帶參數(shù)邪铲,或者參數(shù)本身不需要外界上下文信息。
- 可以使用反射創(chuàng)建多個(gè)對(duì)象无拗。
枚舉
使用枚舉的好處是不能通過(guò)反射實(shí)例化對(duì)象带到,原因是枚舉類實(shí)際上是一個(gè)抽象類,比如
public enum Singleton {}
相當(dāng)于
public abstract class Singleton extends Enum {}
抽象類是不能被實(shí)例化英染,即使是用反射也一樣揽惹。枚舉的實(shí)現(xiàn)方法如下:
public enum Singleton {
INSTANCE;
private Singleton() {
}
}
使用這種方法的缺陷是:
- 如果構(gòu)造函數(shù)需要傳入不是常量的參數(shù)則無(wú)法使用。
抽象類匿名子類
這種方式也是用來(lái)解決反射問(wèn)題的四康,從枚舉式中得到的靈感搪搏,網(wǎng)絡(luò)上提到這種方式的文章較少。實(shí)現(xiàn)方法如下:
public abstract class Singleton {
private static Singleton sInstance;
public synchronized static Singleton getInstance(Context context) {
if(sInstance == null) {
sInstance = new Singleton(context) {};
}
return sInstance;
}
private Singleton(Context context) {
}
}
這種方式完美解決了各種問(wèn)題闪金,這里又使用了懶漢式疯溺,也可以替換成其他實(shí)現(xiàn)方式论颅。