java代碼優(yōu)化——用私有構(gòu)造器或者枚舉類型強(qiáng)化Singleton屬性

Singleton只不過(guò)是指僅僅實(shí)例化一次的類。Singleton通常被用來(lái)代表那些本質(zhì)上唯一的系統(tǒng)組件,比如窗口管理器或者文件系統(tǒng)。使類成為Singleton會(huì)使它的客戶端測(cè)試變得十分困難,因?yàn)闊o(wú)法給Singleton替換模擬實(shí)現(xiàn),除非它實(shí)現(xiàn)一個(gè)充當(dāng)其類型的接口。

單例的實(shí)現(xiàn)

在[Java]1.5發(fā)行版本之前,實(shí)現(xiàn)Singleton有兩種方法。這兩種方法都要把構(gòu)造器保持為私有的,并導(dǎo)出公有的靜態(tài)成員,以便允許客戶端能夠訪問(wèn)該類的唯一實(shí)例。

第一種:公有靜態(tài)final成員
public class Singleton {
    public static final Singleton INSTANCE = new Singleton();
    private Singleton() {
        // 初始化操作
    }
    public void execute() {
        System.out.println("execute Singleton");
    }
} 

但是,這種寫(xiě)法可以通過(guò)反射機(jī)制調(diào)用私有的構(gòu)造器。

Class singletonClass = Class.forName("effactive.java.eff003.Singleton");
Constructor constructor = singletonClass.getDeclaredConstructor();
constructor.setAccessible(Boolean.TRUE);
Singleton singleton = (Singleton) constructor.newInstance();
singleton.execute();

為了避免反射機(jī)制調(diào)用私有的構(gòu)造器需要在修改私有的構(gòu)造器,當(dāng)試圖創(chuàng)建第二個(gè)實(shí)例是拋出異常。

private Singleton() {
    if (INSTANCE != null){
        throw new IllegalStateException("Already instantiated");
    }
}

這樣,在創(chuàng)建第二個(gè)實(shí)例是就會(huì)拋出異常。保證始終只有一個(gè)實(shí)例。

第二種:公有的靜態(tài)工廠方法
public class Singleton2 {
    private static final Singleton2 INSTANCE = new Singleton2();
    private Singleton2() {
        if (INSTANCE != null){
            throw new IllegalStateException("Already instantiated");
        }
    }
    public static Singleton2 getInstance(){
        return  INSTANCE;
    }
    public void execute() {
        System.out.println("execute Singleton2");
    }
}

靜態(tài)工廠方法要比第一種公有靜態(tài)final成員靈活一些。可以在不改變API的前提下,改變?cè)擃愂欠袷菃卫南敕?。但是,這種寫(xiě)法仍可以通過(guò)反射機(jī)制調(diào)用私有的構(gòu)造器。

在Java 1.5之后我們有第三種。

第三種:?jiǎn)蝹€(gè)元素的枚舉類型

public enum Singleton3 {
    INSTANCE;
    public void execute() {
        System.out.println("execute Singleton3");
    }
}

使用的話:

Singleton3.INSTANCE.execute();

由于Java的枚舉類型實(shí)現(xiàn)了Serializable接口,默認(rèn)是可以序列化的,而且還能包證反序列化之后不會(huì)重新創(chuàng)建一個(gè)實(shí)例。

單例的序列化

如果我們將單例序列化,那么當(dāng)我們反序列化,還會(huì)單例嗎?

對(duì)于第一種和第二種來(lái)說(shuō),反序列化之后,我們相當(dāng)與重新創(chuàng)建了一個(gè)新的實(shí)例。不能再保證單例了。

對(duì)于第三種,由于JAVA在枚舉類型反序列化時(shí)候與一般類的不一樣,可以保證反序列化之后的依然是單例。
下面我們來(lái)解決第一種和第二種反序列化的問(wèn)題。

public class Singleton4 implements Serializable{
    private static final Singleton4 INSTANCE = new Singleton4();
    private Singleton4() {
        if (INSTANCE != null){
            throw new IllegalStateException("Already instantiated");
        }
    }
    public static Singleton4 getInstance(){
        return  INSTANCE;
    }
   //需要該方法來(lái)保證反序列化后仍為同一對(duì)象
    private Object readResolve() {
        return Singleton4.INSTANCE;
    }
    public void execute() {
        System.out.println("execute Singleton4");
    }
}

下面是測(cè)試的代碼

File file = new File("/home/pj/person.out");
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
oout.writeObject(Singleton4.getInstance());
oout.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
Singleton4 singleton4 = (Singleton4) oin.readObject();
oin.close();
System.out.println(Singleton4.getInstance());
System.out.println(singleton4);
System.out.println(Singleton4.getInstance() == singleton4);

總結(jié):

對(duì)比來(lái)看,單元素的枚舉類型應(yīng)該是實(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)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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