法則:用私有構造器或枚舉類型強化Singleton屬性
實現(xiàn)Singleton的三種方法:
- 把構造器保持為私有的,并導出公有的靜態(tài)成員。
- 把構造器保持為私有的,并導出公有的靜態(tài)工廠方法。
- 使用單元素的枚舉類型來實現(xiàn)。
直接調用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;
}
}
以上兩種方法可能存在如下問題:
享有特權的客戶端可以借助AccessibleObject.setAccessible方法,通過反射機制調用私有構造器。
若Singleton類實現(xiàn)可序列化的,會導致每次反序列化都會產(chǎn)生一個新的實例。
針對問題一,我們通常會在私有的構造函數(shù)中,添加一個判斷,若實例已存在,則拋出一個異常。
private Singleton(){
if (INSTANCE != null){
throw new UnsupportedOperationException("Instance already exist");
}
}
- 針對問題二,我們先看下沒有做任何處理時的效果:
a. 讓Singleton類實現(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. 進行序列化和反序列化:
public class SingletonTest {
public static void main(String[] args){
try {
serialize(Singleton.getInstance(),"singleton");
deserialize("singleton");
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 實現(xiàn)序列化
* @param singleton 傳入的Singleton對象
* @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);
//打印序列化的對象
System.out.println("Before serialize: " + singleton);
oos.flush();
}
/**
* 實現(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);
//讀取到反序列化對象
Singleton singleton = (Singleton) ois.readObject();
//打印對象
System.out.println("After deserialize: " + singleton);
return singleton;
}
}
c. 實現(xiàn)結果如下:
Before serialize: single_mode.Singleton@14ae5a5
After deserialize: single_mode.Singleton@448139f0
我們可以看到序列化前后兩個對象實例并不是一樣的。
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制,防止多次序列化,即使是在面對復雜的序列化或者反射攻擊時。