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

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

單例的實現(xiàn)

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

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

但是,這種寫法可以通過反射機制調(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();

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

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

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

第二種:公有的靜態(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成員靈活一些??梢栽诓桓淖傾PI的前提下,改變該類是否是單例的想法。但是,這種寫法仍可以通過反射機制調(diào)用私有的構(gòu)造器。

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

第三種:單個元素的枚舉類型

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

使用的話:

Singleton3.INSTANCE.execute();

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

單例的序列化

如果我們將單例序列化,那么當我們反序列化,還會單例嗎?

對于第一種和第二種來說,反序列化之后,我們相當與重新創(chuàng)建了一個新的實例。不能再保證單例了。

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

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;
    }
   //需要該方法來保證反序列化后仍為同一對象
    private Object readResolve() {
        return Singleton4.INSTANCE;
    }
    public void execute() {
        System.out.println("execute Singleton4");
    }
}

下面是測試的代碼

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é):

對比來看,單元素的枚舉類型應該是實現(xiàn)單例的最佳方式了。

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

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

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