2012-07-31 124 views
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"); 

...然后系列化做工精细。

有人可以告诉我为什么会发生,这些陈述之间有什么区别?我认为他们应该同样工作,但显然我错过了一些东西。

回答

10

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

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

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

您的地图初始化,然后由此宣告了初始化器块:

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

并在那里你打电话给你的人口方法。

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

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

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

+1

通过'静态初始化程序',你的意思是'实例初始化程序? – 2012-07-31 09:21:40

+0

那么预期的封闭类会是什么? – Shark 2012-07-31 09:22:25

+0

@ Eng.Fouad - 哎呀。修正 – 2012-07-31 09:24:00

0

我想补充@布赖恩·阿格纽的回答这个建议:

我有地方,我需要稍微不同的行为了一个对象的情况下,所以我用一个匿名内部类扩展它的功能,你在做例。外部类是一个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传递到它的克隆。要说简单的话,这是很多的话,但我希望自己早早得到这个建议。

相关问题