问题描述
请考虑以下三个类:
- EntityTransformer 包含将 Entity关联的地图(带有字符串)
- 实体是一个包含ID(由equals / hashcode使用)的对象,并且包含对 EntityTransformer (请注意循环依赖项)
- SomeWrapper 包含一个 EntityTransformer ,并维护一个与 Entity关联的地图的标识符和相应的 Entity 对象。
- EntityTransformer contains a map associating an Entity with a String
- Entity is an object containing an ID (used by equals / hashcode), and which contains a reference to an EntityTransformer (note the circular dependency)
- SomeWrapper contains an EntityTransformer, and maintains a Map associating Entity's identifiers and the corresponding Entity object.
以下代码将创建一个EntityTransformer和一个包装器,将两个实体添加到包装器中,对其进行序列化,反序列化并测试两个实体的存在:
The following code will create an EntityTransformer and a Wrapper, add two entities to the Wrapper, serialize it, deserialize it and test the presence of the two entitites:
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));
}
输出为:
false
true
所以基本上,序列化以某种方式失败了,因为映射应该包含两个实体作为Key。我怀疑Entity和EntityTransformer之间的循环依赖关系,实际上,如果我将Entity的EntityManager实例变量设为静态,它就可以工作。
So basically, the serialization failed somehow, as the map should contain both entities as Keys. I suspect the cyclic dependency between Entity and EntityTransformer, and indeed if I make static the EntityManager instance variable of Entity, it works.
问题1 :考虑到我对这种周期性依赖的坚持,我该如何克服这个问题?
Question 1: given that I'm stuck with this cyclic dependency, how could I overcome this issue ?
另一个很奇怪的事情:如果我删除了在包装器中维护标识符和实体之间关联的Map,那么一切正常...
Another very weird thing: if I remove the Map maintaining an association between identifiers and Entities in the Wrapper, everything works fine... ??
问题2 :有人了解这里发生了什么吗?
Question 2: someone understand what's going on here ?
如果您想测试以下内容,Balllow是完整功能的代码:
Bellow is a full functional code if you want to test it:
在此先感谢您的帮助:)
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;
}
}
编辑
此问题的潜在影响有一个很好的摘要:
There is a good summary of what is potentially in play for this issue:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4957674
如果某个键包含(直接或间接)对
映射的循环引用,则在$期间可能执行以下顺序b $ b反序列化---如果密钥在
哈希表之前写入对象流:
If a key contains (directly or indirectly) a circular reference to the map, the following order of execution is possible during deserialization --- if the key was written to the object stream before the hashmap:
- 实例化密钥
- 反序列化键的属性
2a。反序列化HashMap(由密钥直接或间接指向)
2a-1。实例化HashMap
2a-2。读取键和值
2a-3。在键上调用hashCode()以重新哈希地图
2b。反序列化键的剩余属性
由于2a-3在2b之前执行,hashCode()可能返回错误的
答案,因为键的属性尚未完全反序列化
。
Since 2a-3 is executed before 2b, hashCode() may return the wrong answer, because the key's attributes have not yet been fully deserialized.
现在,这不能完全解释为什么可以解决此问题如果从包装器中删除了HashMap,或移到EntityTransformer类。
Now that does not explain fully why the issue can be fixed if the HashMap from Wrapper is removed, or move to the EntityTransformer class.
推荐答案
这是循环初始化的问题。虽然Java序列化可以处理任意周期,但初始化必须以某种顺序进行。
This is a problem with circular initialisation. Whilst Java Serialisation can handle arbitrary cycles, the initialisation has to happen in some order.
AWT中也存在类似的问题,其中 Component
( Entity
)包含对其父级 Container
( EntityTransformer
)。 AWT的作用是在 Component
transient
中创建父引用。
There's a similar problem in AWT where Component
(Entity
) contains a reference to its parent Container
(EntityTransformer
). What AWT does is to make the parent reference in Component
transient
.
transient Container parent;
所以现在每个 Component
都可以完成初始化在 Container.readObject
将其添加回之前:
So now each Component
can complete its initialisation before Container.readObject
adds it back in:
for(Component comp : component) {
comp.parent = this;
这篇关于HashMap的奇怪序列化行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!