Java設(shè)計(jì)模式之單例

單例,顧名思義,就是整個(gè)進(jìn)程運(yùn)行過(guò)程中只有一個(gè)實(shí)例,單例對(duì)象的特征一般如下:

  1. 生命周期較長(zhǎng),通常在進(jìn)程結(jié)束前都不需要釋放;或者使用頻率很高,反復(fù)創(chuàng)建不如犧少量?jī)?nèi)存來(lái)?yè)Q取性能。
  2. 避免對(duì)共享資源的多重占用。

Java中實(shí)現(xiàn)單例一般有餓漢式和懶漢式兩種主要方式。其中懶漢式又有多種拓展做法,如雙重檢測(cè)式、靜態(tài)內(nèi)部類(lè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() {
        
    }
}

這種方式利用了類(lèi)加載過(guò)程是唯一的并且安全的特性,但這種方式也有以下的限制或弊端:

  1. 構(gòu)造函數(shù)不能帶參數(shù),或者參數(shù)本身不需要外界上下文信息。
  2. 可以使用反射創(chuàng)建多個(gè)對(duì)象。
  3. 如果對(duì)象始終沒(méi)有被使用到(類(lèi)使用到了),則白白浪費(fèi)了內(nèi)存。

另外這種實(shí)現(xiàn)方法還有一種變體,就是在類(lèi)靜態(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),但也存在以下限制或弊端:

  1. 可以使用反射創(chuàng)建多個(gè)對(duì)象。
  2. 每次調(diào)用getInstance()方法都要加鎖,在高并發(fā)訪(fǎng)問(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ì)加鎖,提高了訪(fǎng)問(wèn)效率,但也存在以下限制或弊端:

  1. 可以使用反射創(chuàng)建多個(gè)對(duì)象。

靜態(tài)內(nèi)部類(lè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();
    }
}

這種方式有以下的限制或弊端:

  1. 構(gòu)造函數(shù)不能帶參數(shù),或者參數(shù)本身不需要外界上下文信息。
  2. 可以使用反射創(chuàng)建多個(gè)對(duì)象。

枚舉

使用枚舉的好處是不能通過(guò)反射實(shí)例化對(duì)象,原因是枚舉類(lèi)實(shí)際上是一個(gè)抽象類(lèi),比如

public enum Singleton {}

相當(dāng)于

public abstract class Singleton extends Enum {}

抽象類(lèi)是不能被實(shí)例化,即使是用反射也一樣。枚舉的實(shí)現(xiàn)方法如下:

public enum Singleton {
    INSTANCE;
    
    private Singleton() {
        
    }
}

使用這種方法的缺陷是:

  1. 如果構(gòu)造函數(shù)需要傳入不是常量的參數(shù)則無(wú)法使用。

抽象類(lèi)匿名子類(lèi)

這種方式也是用來(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)方式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容