MST

星途 面试题库

面试题:Java中如何通过反序列化破坏单例模式

在Java单例模式中,简述反序列化操作是怎样打破单例模式的原理。假设已有一个饿汉式单例类,写出可能导致反序列化破坏单例的示例代码。
23.5万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试
  1. 反序列化打破单例模式的原理
    • 正常情况下,单例模式通过各种机制(如饿汉式的类加载时初始化、懒汉式的双重检查锁等)保证在整个应用程序中只有一个实例。
    • 然而,在反序列化时,JVM 会通过调用 ObjectInputStreamreadObject 方法创建一个新的对象实例。它并不会复用已有的单例实例,而是基于类的字节码信息创建一个新的对象,这就导致了多个实例的产生,从而打破了单例模式。
  2. 饿汉式单例类及可能导致反序列化破坏单例的示例代码
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。最后通过比较 singleton1singleton2 的内存地址发现它们是不同的实例,这表明反序列化打破了单例模式。

为了防止反序列化破坏单例,可以在 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 会调用该方法返回已有的单例实例,从而保证单例模式不被破坏。