在Java中,單例類是一種特殊的類,它只能有一個實例。為了確保單例類的唯一性,我們通常會使用雙重檢查鎖定(Double-Checked Locking)或者枚舉(Enum)來實現(xiàn)。然而,當單例類實現(xiàn)了Serializable
接口時,可以通過序列化和反序列化來創(chuàng)建多個實例。這與單例類的設(shè)計原則相悖,因此我們需要處理這種情況。
下面是一個簡單的單例類實現(xiàn),同時處理了序列化和反序列化的問題:
import java.io.*;
public class Singleton implements Serializable {
private static final long serialVersionUID = 1L;
// 創(chuàng)建一個私有靜態(tài)變量,用于存儲單例實例
private static volatile Singleton instance;
// 將構(gòu)造方法設(shè)置為私有,防止外部實例化
private Singleton() {
// 防止通過反射創(chuàng)建多個實例
if (instance != null) {
throw new IllegalStateException("Singleton instance already exists!");
}
}
// 提供一個全局訪問點
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
// 為了處理序列化和反序列化的問題,需要實現(xiàn)readResolve方法
protected Object readResolve() {
return getInstance();
}
}
在這個實現(xiàn)中,我們使用了volatile
關(guān)鍵字來確保instance
變量的可見性。同時,我們在構(gòu)造方法中添加了一個檢查,防止通過反射創(chuàng)建多個實例。最后,我們實現(xiàn)了readResolve()
方法,它會在反序列化時被調(diào)用。在這個方法中,我們返回單例實例,從而確保反序列化時不會創(chuàng)建新的實例。
下面是一個測試代碼,展示了如何使用這個單例類:
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Singleton singleton1 = Singleton.getInstance();
// 序列化singleton1對象到文件
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
oos.writeObject(singleton1);
oos.close();
// 從文件反序列化得到新的對象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singleton.ser"));
Singleton singleton2 = (Singleton) ois.readObject();
ois.close();
System.out.println("singleton1: " + singleton1);
System.out.println("singleton2: " + singleton2);
// 輸出結(jié)果:兩個對象相等,說明反序列化沒有創(chuàng)建新的實例
System.out.println("singleton1 == singleton2: " + (singleton1 == singleton2));
}
}
運行這個測試代碼,你會看到singleton1
和singleton2
是相等的,這證明了反序列化沒有創(chuàng)建新的實例,而是返回了已經(jīng)存在的單例實例。