1、谨慎地实现Serializable接口
a、实现Serializable接口而付出的代价是,一旦一个类被发布,就大大降低了“改变这个类的实现”的灵活性
系列化会使类的演变受到限制,这种限制与流的唯一标示符有关,通常也称做序列版本UID。如果你没有显示的声明序列版本,系统就会自动地根据这个类来调用一个复杂的元素过程,从而在运行时产生标识号(这个值首类名、实现的接口名称、以及所有公有的和受保护的成员的名称所影响)。
b、实现Serializable接口的第二个代价是,它增加了出现Bug和安全漏洞的可能性。
系列化机制是一种语言之外的对象创建机制。
c、实现Serializable接口的第三个代价是,随着发行新的版本,相关测试负担也增加了。
很重要的一点:要检查是否可以“在新版本中序列化一个实例,然后在旧版本中反序列化”,反之亦然。
根据经验:如Date和BigInteger这样的值类应该实现Serializable接口,大多数集合类也该如此。代表活动实体的类,如线程池,一般不应该实现Serializable。
2、考虑使用自定义的序列化形式
public final class StringList implements Serializable {
private transient int size = 0;
private transient Entry head;
private final class Entry implements Serializable {
// 默认的序列化形式会镜像出列表中的所有项,以及这些项之间的所有双相链接。
String data;
Entry next;
Entry previous;
}
public final void add(String s){}
private void writeObject(ObjectOutputStream s){
s.defaultWriteObject();
s.writeInt(size);
for(Entry e = head; e != null; e = e.next){
s.writeObject(e);
}
}
private void readObject(ObjectInputSream s){
s.defaultReadObject();
int numElements = s.readInt();
for (int i = 0; i < numElements; i++) {
add((String)s.readObject());
}
}
}
无论你是否使用默认的序列化形式,如果在读取整个对象状态的任何其他方法上强制任何同步,则也必须在对象序列化上强制这种同步。
3、保护性地编写readObject方法
4、对于实例控制,枚举类型优先于readResolve
readResolve特性允许你用readObject创建的实例代替另一个实例。对于一个正在被反序列化的对象,如果它的类定义了readResolve方法,并且具备正确的声明,那么在反序列化之后,新建对象上的readResolve方法将被调用。然后,该方法返回的对象引用将被返回,取代新建的对象。
5、考虑用序列化代理代替序列化实例
为可序列化的类设计一个私有的静态嵌套类,精确地表示外围类的实例的逻辑状态。这个嵌套类被称做序列化代理,它应该有一个单独的构造器,其参数就是那个外围类。这个构造器从它的参数中复制数据:它不需要进行任何一致性检查和保护性拷贝。从设计角度看,序列化代理的默认序列化形式是外围类最爱好的序列化形式。外围类及其序列代理都必须声明实现Serializable接口。