【问题标题】:Serializing maps which are initialized in constructors序列化在构造函数中初始化的映射
【发布时间】:2012-07-29 00:30:53
【问题描述】:

我刚刚遇到了一个与 Java 序列化相关的有趣问题。

看来如果我的地图是这样定义的:

Map<String, String> params = new HashMap<String, String>() {{
  put("param1", "value1");
  put("param2", "value2");
}};

我尝试使用 ObjectOutputStream 将其序列化为文件:

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(outputFile));
oos.writeObject(params);

...我得到 java.io.NotSerializableException。

但是,如果我以标准方式将值放入地图:

Map<String, String> params = new HashMap<String, String>();
params.put("param1", "value1");
params.put("param2", "value2");

...然后序列化工作正常。

谁能告诉我为什么会发生这种情况以及这些陈述之间有什么区别?我认为它们应该工作相同,但显然我错过了一些东西。

【问题讨论】:

    标签: java serialization hashmap


    【解决方案1】:

    第一个例子是创建一个匿名内部类。怎么样?

    Map<String, String> params = new HashMap<String, String>() {};
    

    将创建一个从HashMap 派生的新类(注意以下大括号,您可以在其中放置方法、成员等)

    你的地图初始化然后声明一个初始化块:

    Map<String, String> params = new HashMap<String, String>() { 
                                                                 { // here } 
                                                               };
    

    并且你调用你的填充方法。

    这个习语很好,但你必须知道你正在创建一个新类,而不仅仅是一个新对象。

    因为这个类是一个内部类,它会有一个隐含的this 指向包含外部类的指针。您的匿名类将是可序列化的,因为它是从可序列化类派生的。但是,您的外部类(由 this 指针引用)不是。

    XStream 等工具通过反射序列化为 XML,会发现 this 指针并尝试序列化周围的对象,这同样令人困惑。

    【讨论】:

    • static initialiser,你的意思是instance initializer
    • 那么预期的封闭类是什么?
    • @Shark - 我只是指包含上述派生 HashMap 定义的类
    • 哦,所以假设如果它在 MainApp 类中实例化,我们会得到 java.io.NotSerializableException,因为 MainApp 也没有实现 Serializable?有趣。
    • 那么尽管他创建了一个内部类,但他如何做到可序列化?
    【解决方案2】:

    我想用这个建议来补充@Brian Agnew 的回答:

    我有一个案例,我需要一个对象的行为略有不同,所以我用匿名内部类扩展了它的功能,就像你在示例中所做的那样。外部类是一个 GUI 应用程序,我没有使其可序列化,因为这不是必需的,因此就像@Brian 所说,没有匿名内部类可以序列化,即使它们扩展的类是可序列化的。

    在这种情况下,您只需为类反序列化和再次序列化时定义不同的行为。如果您有一个具有特定构造函数的类,请在您的类中使用这样的方法:

    public FunctionalObject getNewFunctionalObject (String param1, String param2) {
        // Use an anonymous inner class to extend the behavior
        return new FunctionalObject (param1, param2) {
            { 
                // Initialization block code here
            }
            // Extended behavior goes here
        };
    }
    

    所以在反序列化的时候,可以这样调用:

    FunctionalObject fo = (FunctionalObject) objectInputStream.readObject ();
    fo = getNewFunctionalObject(fo.getParam1(), fo.getParam2());
    

    序列化时,您需要创建一个 new 对象,它是旧对象的克隆。有些类内置了这种行为,而在其他类中,您必须专门定义它。对于序列化,如果你有一个可以克隆它的构造函数,或者你的类定义了clone 方法,你可以这样做:

    objectOutputStream.writeObject ( fo.clone() );
    

    然后,该对象的clone 将不再是对您的匿名内部类的引用,而是对该对象的实际副本的引用,该副本是可序列化的。

    在您的示例中,您可以这样做:

    // Assuming objectOutputStream has already been defined
    Map<String, String> params = new HashMap<String, String>() {{
        put("param1", "value1");
        put("param2", "value2");
    }};
    objectOutputStream.writeObject (new HashMap<String,String> (params));
    

    这是可行的,因为HashMap 类有一个构造函数,该构造函数将返回传递给它的任何HashMap 的克隆。说些简单的话是很多话,但我希望我自己能早点得到这个建议。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-23
      相关资源
      最近更新 更多