转自:http://www.examda.com/JAVA/Instructs/060617/090719996.html
这个程序创建了一个对象并且 检查 它是否遵从某个类的不变规则(invariant)。然后该程序序列化这个对象,之后将其反序列化,然后再次检查反序列化得到的副本是否也遵从这个规则。它会遵从这个规则吗?如果不是的话,又是为什么呢?import java.util.*;
import java.io.*;
public class SerialKiller {
public static void main(String[] args) {
Sub sub = new Sub(666);
sub.checkInvariant();
Sub copy = (Sub) deepCopy(sub);
copy.checkInvariant();
}
// Copies its argument via serialization (See Puzzle 80)
static public Object deepCopy(Object obj) {
try {
ByteArrayOutputStream bos =
new ByteArrayOutputStream();
new ObjectOutputStream(bos).writeObject(obj);
ByteArrayInputStream bin =
new ByteArrayInputStream(bos.toByteArray());
return new ObjectInputStream(bin).readObject();
} catch(Exception e) {
throw new IllegalArgumentException(e);
}
}
}
class Super implements Serializable {
final Setset = new HashSet();
}
final class Sub extends Super {
private int id;
public Sub(int id) {
this.id = id;
set.add(this); // Establish invariant
}
public void checkInvariant() {
if (!set.contains(this))
throw new AssertionError("invariant violated");
}
public int hashCode() {
return id;
}
public boolean equals(Object o) {
return (o instanceof Sub) && (id == ((Sub)o).id);
}
}
程序中除了使用了序列化之外,看起来很简单,super类有一个单独的set类型的域,Sub类添加了另外一个int类型的域。Super和Sub都不需要定制的序列化形式。那么什么东西会出错呢?
其实有很多。对于5.0版本,运行该程序会得到如下的“堆轨迹”(stack trace):
Exception in thread “main” AssertionError
at Sub.checkInvariant(SerialKiller.java:41)
at SerialKiller.main(SerialKiller.java:10)序列化和反序列化一个Sub实例会产生一个被破坏的副本。为什么呢?阅读程序并不会帮助你找出原因,因为真正引起问题的代码在其他地方。错误是由HashSet的readObject方法引起的。在某些情况下,这个方法会间接地调用某个未初始化对象的被覆写的方法。为了组装(populate)正在被反序列化的散列集合,HashSet.readObject调用了HashMap.put方法,而它会去调用每个键(key)的hashCode方法。由于整个对象图(object graph)正在被反序列化,并没有什么可以保证每个键在它的hashCode方法被调用的时候已经被完全初始化了。实际上,这很少会成为一个问题,但是有时候它会造成绝对的混乱。这个缺陷会在正在被反序列化的对象图的某些循环中出现。