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");
}
}
但是,這種寫法可以通過(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è)擃愂欠袷菃卫南敕ā5桥穑@種寫法仍可以通過(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)單例的最佳方式了猛铅。