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)單例的最佳方式了。