ecn*_*niv 5 java serialization deserialization
考虑以下三个类:
以下代码将创建一个EntityTransformer和一个Wrapper,将两个实体添加到Wrapper,对其进行序列化,对其进行反序列化并测试两个权限的存在:
public static void main(String[] args)
throws Exception {
EntityTransformer et = new EntityTransformer();
Wrapper wr = new Wrapper(et);
Entity a1 = wr.addEntity("a1"); // a1 and a2 are created internally by the Wrapper
Entity a2 = wr.addEntity("a2");
byte[] bs = object2Bytes(wr);
wr = (SomeWrapper) bytes2Object(bs);
System.out.println(wr.et.map);
System.out.println(wr.et.map.containsKey(a1));
System.out.println(wr.et.map.containsKey(a2));
}
Run Code Online (Sandbox Code Playgroud)
输出是:
{a1 = whatever-a1,a2 = whatever-a2}
假
真正
所以基本上,序列化以某种方式失败,因为映射应该包含两个实体作为键.我怀疑Entity和EntityTransformer之间存在循环依赖关系,事实上如果我将Entity的EntityManager实例变量设为静态,它就可以工作.
问题1:鉴于我坚持这种循环依赖,我怎么能克服这个问题?
另一个非常奇怪的事情:如果我删除地图维护包装器中的标识符和实体之间的关联,一切正常......?
问题2:有人了解这里发生了什么?
如果你想测试它,贝娄是一个完整的功能代码:
在此先感谢您的帮助 :)
public class SerializeTest {
public static class Entity
implements Serializable
{
private EntityTransformer em;
private String id;
Entity(String id, EntityTransformer em) {
this.id = id;
this.em = em;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Entity other = (Entity) obj;
if ((this.id == null) ? (other.id != null) : !this.id.equals(
other.id)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 97 * hash + (this.id != null ? this.id.hashCode() : 0);
return hash;
}
public String toString() {
return id;
}
}
public static class EntityTransformer
implements Serializable
{
Map<Entity, String> map = new HashMap<Entity, String>();
}
public static class Wrapper
implements Serializable
{
EntityTransformer et;
Map<String, Entity> eMap;
public Wrapper(EntityTransformer b) {
this.et = b;
this.eMap = new HashMap<String, Entity>();
}
public Entity addEntity(String id) {
Entity e = new Entity(id, et);
et.map.put(e, "whatever-" + id);
eMap.put(id, e);
return e;
}
}
public static void main(String[] args)
throws Exception {
EntityTransformer et = new EntityTransformer();
Wrapper wr = new Wrapper(et);
Entity a1 = wr.addEntity("a1"); // a1 and a2 are created internally by the Wrapper
Entity a2 = wr.addEntity("a2");
byte[] bs = object2Bytes(wr);
wr = (Wrapper) bytes2Object(bs);
System.out.println(wr.et.map);
System.out.println(wr.et.map.containsKey(a1));
System.out.println(wr.et.map.containsKey(a2));
}
public static Object bytes2Object(byte[] bytes)
throws IOException, ClassNotFoundException {
ObjectInputStream oi = null;
Object o = null;
try {
oi = new ObjectInputStream(new ByteArrayInputStream(bytes));
o = oi.readObject();
}
catch (IOException io) {
throw io;
}
catch (ClassNotFoundException cne) {
throw cne;
}
finally {
if (oi != null) {
oi.close();
}
}
return o;
}
public static byte[] object2Bytes(Object o)
throws IOException {
ByteArrayOutputStream baos = null;
ObjectOutputStream oo = null;
byte[] bytes = null;
try {
baos = new ByteArrayOutputStream();
oo = new ObjectOutputStream(baos);
oo.writeObject(o);
bytes = baos.toByteArray();
}
catch (IOException ex) {
throw ex;
}
finally {
if (oo != null) {
oo.close();
}
}
return bytes;
}
}
Run Code Online (Sandbox Code Playgroud)
编辑
关于此问题可能起到的作用有一个很好的总结:http: //bugs.sun.com/bugdatabase/view_bug.do?video_id = 4957674
问题是HashMap的readObject()实现,为了重新散列映射,调用其某些键的hashCode()方法,无论这些键是否已完全反序列化.
如果一个键包含(直接或间接)对映射的循环引用,则在反序列化期间可以执行以下执行顺序---如果在hashmap之前将键写入对象流:
- 实例化密钥
- 反序列化键的属性2a.反序列化HashMap(由密钥直接或间接指向)2a-1.实例化HashMap 2a-2.读取键和值2a-3.在键上调用hashCode()以重新散列映射2b.反序列化密钥的剩余属性
由于2a-3在2b之前执行,hashCode()可能会返回错误的答案,因为键的属性尚未完全反序列化.
现在,这并没有完全解释为什么如果删除Wrapper中的HashMap,或者移动到EntityTransformer类,可以修复该问题.
这是循环初始化的问题。虽然 Java 序列化可以处理任意循环,但初始化必须按某种顺序进行。
AWT 中也存在类似问题,其中Component( ) 包含对其父级( )Entity的引用。AWT 所做的就是在.ContainerEntityTransformerComponent transient
transient Container parent;
Run Code Online (Sandbox Code Playgroud)
所以现在每个人都可以在将其添加回Component之前完成其初始化:Container.readObject
for(Component comp : component) {
comp.parent = this;
Run Code Online (Sandbox Code Playgroud)