我刚遇到一个与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");
...然后序列化工作正常。
有人可以告诉我它为什么会发生这些声明之间有什么区别?我认为他们应该一样,但显然我错过了一些东西。
答案 0 :(得分:10)
第一个例子是创建一个匿名内部类。怎么样?
Map<String, String> params = new HashMap<String, String>() {};
会创建一个派生自HashMap
的新类(请注意以下大括号,您可以在其中放置方法,成员等。)
您的地图初始化然后声明初始化块:
Map<String, String> params = new HashMap<String, String>() {
{ // here }
};
并且您可以调用您的填充方法。
这个成语很好,但是你必须要知道你正在创建一个新类,而不仅仅是一个新对象。
因为这个类是一个内部类,所以它有一个指向包含外部类的隐式this
指针。您的匿名类将是可序列化的,因为它派生自可序列化的类。但是,您的外部类(由this
指针引用)不是。
像XStream
这样通过反射串行化为XML的工具将发现this
指针并尝试序列化周围的对象,这同样令人困惑。
答案 1 :(得分: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
的克隆。这说了很多简单的话,但我希望自己能早点得到这个建议。