反序列化后丢失对象的引用

时间:2012-07-17 13:21:39

标签: java

假设我有两个HashMaphashMapFoohashMapBar

我在两个地图中创建了一些对象objCakeput,因此每个地图都引用了objCake,当我对objCake进行一些更改时,无论哪个地图我从中获取它,我得到了我的对象的正确状态。

在我序列化两个地图并反序列化之后,我遇到问题,我的对象objCake已成为两个不同的对象!我在hashMapFoo中更改了其状态,但在hashMapBar中没有任何反应。 hashMapBar不再包含正确的引用! 所有地图和对象implement Serializable

有人可以解释一下吗?

3 个答案:

答案 0 :(得分:6)

适合我:

public class MapSerializationTest {
    private static class Foo implements Serializable {
    }

    public static void main(String[] args) throws Exception {
        Foo foo = new Foo();

        Map<String, Foo> map1 = new HashMap<String, Foo>();
        map1.put("foo", foo);
        Map<String, Foo> map2 = new HashMap<String, Foo>();
        map2.put("foo", foo);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(map1);
        oos.writeObject(map2);
        oos.close();

        byte[] bytes = baos.toByteArray();

        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        map1 = (Map<String, Foo>) ois.readObject();
        map2 = (Map<String, Foo>) ois.readObject();
        System.out.println(map1.get("foo") == map2.get("foo")); // prints true
    }
}

告诉我们你的代码。您可能在第一个和第二个映射之间的ObjectOutputStream上调用reset()。或者您使用两个不同的ObjectOutputStream实例。

答案 1 :(得分:4)

您可以为HashMaps使用容器,以便它们都属于同一个对象图,否则,在重新创建对象图时,Java无法确定它们是否是同一个对象。毕竟,你将它们序列化并独立地反序列化,不是这样吗?

public class Container implements Serializable {
   private Map<Object, Object> hashMapFoo ;
   private Map<Object, Object> hashMapBar;

  //...
}

如果序列化容器并将其反序列化,则引用应该是您所期望的,因为ObjectInputStream和ObjectOutputStream在序列化/反序列化对象图时会保留对它们的引用。

实施例

这对我有用:

public static void test() {

    class Container implements Serializable {
            Map<String,StringBuilder> map1 = new HashMap<String, StringBuilder>();
            Map<String,StringBuilder> map2 = new HashMap<String, StringBuilder>();
    }

    try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("jedis.bin"))){

        StringBuilder text = new StringBuilder("Hello Elvis");

        Container container = new Container();
        //same object in two different maps
        container.map1.put("one", text);
        container.map2.put("one", text);

        out.writeObject(container);

    }catch(IOException e) {
        System.out.println(e.getMessage());
    }

    try(ObjectInputStream in = new ObjectInputStream(new FileInputStream("jedis.bin"))) {
        Container container = (Container) in.readObject();
        StringBuilder text1 = container.map1.get("one");
        StringBuilder text2 = container.map2.get("one");

        assert text1 == text2 : "text1 and tex2 are not the same reference";

    }catch(Exception e) {
        System.out.println(e.getMessage());
    }
}

答案 2 :(得分:2)

来自:Security in Object Serialization

  

序列化包不能用于重新创建或重新初始化   对象。反序列化字节流可能会导致创建新的   对象 ,但不会覆盖或修改现有内容   对象。

另外 - 使用外部引用并使用它来修改对象状态(而不是从Map获取引用)是一个坏主意。