考虑以下三个类:

  • EntityTransformer 包含将实体与字符串
  • 关联的映射
  • 实体是一个包含ID(由equals/hashcode使用)的对象,并且包含对 EntityTransformer 的引用(请注意循环依赖性)
  • SomeWrapper 包含一个 EntityTransformer ,并维护一个将实体的标识符与相应的实体对象相关联的Map。

  • 以下代码将创建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));
    }
    

    输出为:



    因此,基本上,序列化以某种方式失败了,因为映射应同时包含两个实体作为键。我怀疑Entity和EntityTransformer之间的循环依赖关系,确实,如果我将Entity的EntityManager实例变量设为静态,它就可以工作。

    问题1 :考虑到我对这种循环依赖性的坚持,我该如何克服这个问题?

    另一个非常奇怪的事情:如果我删除了在包装器中维护标识符和实体之间关联的Map,那么一切都会正常进行... ??

    问题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;
    }
    }
    

    编辑

    关于此问题的潜在影响,有一个很好的摘要:
    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4957674



    现在,这还不能完全说明为什么如果删除了Wrapper中的HashMap或将其移至EntityTransformer类,则可以解决此问题的原因。

    最佳答案

    这是循环初始化的问题。尽管Java序列化可以处理任意周期,但初始化必须以某种顺序进行。

    在AWT中存在类似的问题,其中Component(Entity)包含对其父Container(EntityTransformer)的引用。 AWT的工作是在Component transient中创建父引用。

    transient Container parent;
    

    因此,现在每个Component可以在Container.readObject将其添加回之前完成其初始化:
        for(Component comp : component) {
            comp.parent = this;
    

    关于java - HashMap的奇怪序列化行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10031833/

    10-13 09:35