面试题答案
一键面试- 反序列化打破单例模式的原理:
- 正常情况下,单例模式通过各种机制(如饿汉式的类加载时初始化、懒汉式的双重检查锁等)保证在整个应用程序中只有一个实例。
- 然而,在反序列化时,JVM 会通过调用
ObjectInputStream
的readObject
方法创建一个新的对象实例。它并不会复用已有的单例实例,而是基于类的字节码信息创建一个新的对象,这就导致了多个实例的产生,从而打破了单例模式。
- 饿汉式单例类及可能导致反序列化破坏单例的示例代码:
import java.io.Serializable;
// 饿汉式单例类
class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// 私有构造函数,防止外部实例化
}
public static Singleton getInstance() {
return INSTANCE;
}
}
// 测试反序列化破坏单例的代码
import java.io.*;
public class Main {
public static void main(String[] args) {
// 获取单例实例
Singleton singleton1 = Singleton.getInstance();
// 将单例实例序列化到文件
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton.ser"))) {
oos.writeObject(singleton1);
} catch (IOException e) {
e.printStackTrace();
}
// 从文件中反序列化单例实例
Singleton singleton2 = null;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singleton.ser"))) {
singleton2 = (Singleton) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
// 检查两个实例是否相同
System.out.println("singleton1 == singleton2: " + (singleton1 == singleton2));
}
}
在上述代码中,Singleton
类是饿汉式单例。在 main
方法中,首先获取单例实例 singleton1
并将其序列化到文件。然后从文件中反序列化得到 singleton2
。最后通过比较 singleton1
和 singleton2
的内存地址发现它们是不同的实例,这表明反序列化打破了单例模式。
为了防止反序列化破坏单例,可以在 Singleton
类中添加 readResolve
方法:
class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// 私有构造函数,防止外部实例化
}
public static Singleton getInstance() {
return INSTANCE;
}
// 防止反序列化破坏单例
protected Object readResolve() {
return INSTANCE;
}
}
添加 readResolve
方法后,反序列化时 JVM 会调用该方法返回已有的单例实例,从而保证单例模式不被破坏。