序列化单例和类型安全的枚举
在序列化和反序列时,如果目标对象是唯一的,那么必须加倍小心,这通常会在实现单例和类型安全的枚举时发生。
如果你使用Java 语言的enum结构,那么你就不必担心序列化,它能够正常工作。但是,假设你在维护遗留代码,其中包含下面这样的枚举类型:
public class Orientation{
public static final Orientation HORIZONTAL = new Orientation(1);
public static final Orientation VERTICAL = new Orientation(2);
private int value;
private Orientation(int v){ value = v;}
}
这种风格在枚举被添加到Java 语言中之前是很普遍的。注意,其构造器是私有的。因此,不可能创建出超出Orientation.HORIZONTAL 和 Orientation.VERTICAL 之外的对象。特别是,你可以使用 == 操作符来测试对象的等同性:
if(orientation == Orientation.HORIZONTAL)...
当类型安全的枚举实现 Serializable 接口时,默认的序列化机制是不适用的。假设我们写出一个Orientation 类型的值,并再次将其读回:
import java.io.*;
public class Orientation implements Serializable{
public static final Orientation HORIZONTAL = new Orientation(1);
public static final Orientation VERTICAL = new Orientation(2);
private int value;
private Orientation(int v){ value = v;}
public static void main(String[] args) throws Exception{
Orientation original = Orientation.HORIZONTAL;
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("orientation.dat"));
out.writeObject(original);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("orientation.dat"));
Orientation saved = (Orientation)in.readObject();
System.out.println( Orientation.HORIZONTAL instanceof Orientation);
System.out.println(saved == Orientation.HORIZONTAL);
System.out.println(saved.HORIZONTAL == Orientation.HORIZONTAL);
System.out.println(saved.VERTICAL == Orientation.HORIZONTAL);
System.out.println( saved instanceof Orientation);
}
}
/*
运行结果:
true
false
true
false
true
分析:事实上,saved 的值是Orientation 类型的一个全新的对象,它与任何预定义的常量都不等同。
即使构造器是私有的,序列化机制也可以创建新的对象。
*/
为了解决这个问题,需要定义一种称为readResolve 的特殊序列化方法。如果定义了readResolve 方法,在对象被序列化之后就会调用它。它必须返回一个对象,而该对象之后会成为 readObject 的返回值。在上面的情况中,readResolve 将检查value 域,并返回恰当的枚举常量:
//在上述类中添加如下方法:
protected Object readResolve() throws ObjectStreamException{
if(value == 1)
return Orientation.HORIZONTAL;
if(value == 2)
return Orientation.VERTICAL;
return null;
}
/*
运行结果:
true
true
true
false
true
*/
请记住,向遗留代码中所有类型安全的枚举以及向所有支持单例设计模式的类中添加 readResolve 方法。
以上来源于Java 核心技术卷Ⅱ (原书第九版)