法則:用私有構(gòu)造器或枚舉類型強(qiáng)化Singleton屬性
實(shí)現(xiàn)Singleton的三種方法:
- 把構(gòu)造器保持為私有的,并導(dǎo)出公有的靜態(tài)成員。
- 把構(gòu)造器保持為私有的,并導(dǎo)出公有的靜態(tài)工廠方法。
- 使用單元素的枚舉類型來實(shí)現(xiàn)。
直接調(diào)用INSTANCE
public class Singleton{
public static final Singleton INSTANCE = new Singleton();
private Singleton(){}
}
使用靜態(tài)方法getInstance獲取
public class Singleton implements Serializable{
private static final Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
}
以上兩種方法可能存在如下問題:
享有特權(quán)的客戶端可以借助AccessibleObject.setAccessible方法,通過反射機(jī)制調(diào)用私有構(gòu)造器。
若Singleton類實(shí)現(xiàn)可序列化的,會(huì)導(dǎo)致每次反序列化都會(huì)產(chǎn)生一個(gè)新的實(shí)例。
針對(duì)問題一,我們通常會(huì)在私有的構(gòu)造函數(shù)中,添加一個(gè)判斷,若實(shí)例已存在,則拋出一個(gè)異常。
private Singleton(){
if (INSTANCE != null){
throw new UnsupportedOperationException("Instance already exist");
}
}
- 針對(duì)問題二,我們先看下沒有做任何處理時(shí)的效果:
a. 讓Singleton類實(shí)現(xiàn)Serializable:
public class Singleton implements Serializable{
private static final Singleton INSTANCE = new Singleton();
private Singleton(){
if (INSTANCE != null){
throw new UnsupportedOperationException("Instance already exist");
}
}
public static Singleton getInstance(){
return INSTANCE;
}
}
b. 進(jìn)行序列化和反序列化:
public class SingletonTest {
public static void main(String[] args){
try {
serialize(Singleton.getInstance(),"singleton");
deserialize("singleton");
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 實(shí)現(xiàn)序列化
* @param singleton 傳入的Singleton對(duì)象
* @param filename 傳入的文件名
* @throws IOException
*/
public static void serialize(Singleton singleton, String filename) throws IOException {
FileOutputStream fos = new FileOutputStream(filename);
ObjectOutputStream oos = new ObjectOutputStream(fos);
//寫入文件
oos.writeObject(singleton);
//打印序列化的對(duì)象
System.out.println("Before serialize: " + singleton);
oos.flush();
}
/**
* 實(shí)現(xiàn)反序列化
* @param filename 文件名
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static Singleton deserialize(String filename) throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream(filename);
ObjectInputStream ois = new ObjectInputStream(fis);
//讀取到反序列化對(duì)象
Singleton singleton = (Singleton) ois.readObject();
//打印對(duì)象
System.out.println("After deserialize: " + singleton);
return singleton;
}
}
c. 實(shí)現(xiàn)結(jié)果如下:
Before serialize: single_mode.Singleton@14ae5a5
After deserialize: single_mode.Singleton@448139f0
我們可以看到序列化前后兩個(gè)對(duì)象實(shí)例并不是一樣的。
d. 解決方法:重寫readResolve方法,返回Instance單例:
public class Singleton implements Serializable{
private static final Singleton INSTANCE = new Singleton();
private Singleton(){
if (INSTANCE != null){
throw new UnsupportedOperationException("Instance already exist");
}
}
public static Singleton getInstance(){
return INSTANCE;
}
private Object readResolve(){
return INSTANCE;
}
}
使用單元素的枚舉類型:
public enum Singleton {
INSTANCE;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
使用方法:
public static void main(String[] args){
Singleton.INSTANCE.setName("test");
System.out.println(Singleton.INSTANCE.getName());
}
這種方法在功能上與公有域方法相近,但是它更簡潔,無償?shù)靥峁┝诵蛄谢瘷C(jī)制,防止多次序列化,即使是在面對(duì)復(fù)雜的序列化或者反射攻擊時(shí)。