序列化、反序列化破坏单例模式
我们现在使用静态内部类的方式来创建一个单例类(其他模式同理,除了枚举):
//Singleton.class
//私有构造方法
private Singleton() {}
//定义一个静态内部类
private static class SingletonHolder {
//在内部类中声明并初始化外部类对象
private static final Singleton INSTANCE = new Singleton();
}
//提供getter
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
现在我们在测试类中来尝试破坏这个单例模式
定义一个向桌面文件中写入数据(对象)的方法
public static void writeObject2File() throws Exception {
//1、获取Singleton对象
Singleton instance = Singleton.getInstance();
//2、创建对象输出流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\User\Hexing\Desktop\a.txt"));
//3、写对象
oos.writeObject(instance);
//4、释放资源
oos.close();
}
现在在main方法中直接调用该方法就会报尚未序列化的错误(NotSerializableException)
这就需要Singleton类实现一个序列化接口:
public class Singleton implements Serializable
现在运行weiteObject2File方法就可以成功创建txt文件,并将对象进行写入
有了写入操作,我们再写一个读取操作:
public static void readObjectFromFile() throws Exception {
//1、创建对象输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\User\Hexing\Desktop\a.txt"))
//2、读取对象并打印
Singleton instance = (Singleton)ois.readObject();
//观察这里打印结果,若两次调用打印输出的不同,则说明是不同的对象
System.out.println(instance);
//3、释放资源
ois.close();
}
我们进行两次调用:
readObjectFromFile()
readObjectFromFile()
得到结果:
com.hexiaoxing.pattern.singleton.demo.Singleton@118181a8
com.hexiaoxing.pattern.singleton.demo.Singleton@4d674592
由此可见,两次输出的实例并不相同,所以两次得到的是两个对象,这样就成功破坏了单例模式。
解决方案
想要解决序列化、反序列化对单例模式的破坏,我们需要在Singleton类中添加readResolve()方法,在其被反序列化时调用,返回这个方法的值(对象)。
此前我们没有定义这个方法,返回的就是新new出来的对象,这种破坏方式的原理也在此
//readResolve方法
//当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回
public Object readResolve() {
return SingletonHolder.INSTANCE;
}
这样做的原理是在输入流对象的readObject方法的底层实现中,如果发现输入的是对象类型, 则会调用readOrdinaryObject方法,该方法中会自动执行hasReadResolveMethod方法,来判断类中是否有readResolve方法,如果有,则会去执行invokeReadResolve方法来获取类中专门提供的实例。