A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 廖理 中级黑马   /  2012-5-6 10:49  /  2380 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文


class Super implements Serializable{
// HashSet要放置在父类中会百分百机率出现
// 放置到子类中就不一定会出现问题了
final Set set = new HashSet();
}
class Sub extends Super {
private int id;
public Sub(int id) {
this.id = id;
set.add(this);
}
public int hashCode() {
return id;
}
public boolean equals(Object o) {
return (o instanceof Sub) && (id == ((Sub) o).id);
}
}
public class SerialKiller {
public static void main(String[] args) throws Exception {
Sub sb = new Sub(888);
System.out.println(sb.set.contains(sb));// true
ByteArrayOutputStream bos = new ByteArrayOutputStream();
new ObjectOutputStream(bos).writeObject(sb);
ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray());
sb = (Sub) new ObjectInputStream(bin).readObject();
System.out.println(sb.set.contains(sb));// false
}
}


Hash一类集合都实现了序列化的writeObject()与readObject()方法。这里错误原因是由HashSet的readObject方法引起的。在某些情况下,这个方法会间接地调用某个未初始化对象的被覆写的方法。为了组装正在反序列化的HashSet,HashSet.readObject调用了HashMap.put方法,而put方法会去调用键的hashCode方法。由于整个对象图正在被反序列化,并没有什么可以保证每个键在它的hashCode方法被调用时已经被完全初始化了,因为HashSet是在父类中定义的,而在序列化HashSet时子类还没有开始初始化(这里应该是序列化)子类,所以这就造成了在父类中调用还没有初始完成(此时id为0)的被子类覆写hashCode方法,导致该对象重新放入hash表格的位置与反序列化前不一样了。hashCode返回了错误的值,相应的键值对条目将会放入错误的单元格中,当id被初始化为888时,一切都太迟了。这个程序的说明,包含了HashMap的readObject方法的序列化系统总体上违背了不能从类的构造器或伪构造器(如序列化的readObject)中调用可覆写方法的规则。
如果一个HashSet、Hashtable或HashMap被序列化,那么请确认它们的内容没有直接或间接地引用它们自
身,即正在被序列化的对象。另外,在readObject或readResolve方法中,请避免直接或间接地在正在进行反序列化的对象上调用任何方法,因为正在反序列化的对象处于不稳定状态。

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马