2012-07-31 91 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傳遞到它的克隆。要說簡單的話,這是很多的話,但我希望自己早早得到這個建議。