9

我刚刚遇到了一个与 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");

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

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

4

2 回答 2

10

第一个例子是创建一个匿名内部类。如何 ?

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

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

然后,您的地图初始化会声明一个初始化程序块:

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

并且你称之为你的人口方法。

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

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

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

于 2012-07-31T09:16:37.200 回答
0

我想用这个建议补充@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传递给它的任何内容的克隆。说一些简单的话是很多话,但我希望我自己能早点得到这个建议。

于 2013-11-15T21:15:35.483 回答