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 に答える