web-dev-qa-db-ja.com

互いに参照する2つのオブジェクトをシリアル化および逆シリアル化するとどうなりますか?

より明確にするために、これは簡単な例です:

class A implements Serializable { public B b; }
class B implements Serializable { public A a; }
A a = new A();
B b = new B();
a.b = b;
b.a = a;

では、aオブジェクトとbオブジェクトをファイルにシリアル化し、そのファイルから逆シリアル化するとどうなるでしょうか。

4つのオブジェクト(それぞれ2つ)を取得すると思いました。同一のオブジェクトですが、インスタンスが異なります。

しかし、他に何かがあるかどうか、それが正しいか間違っているかはわかりません。

答える必要のあるテクノロジーがあれば、Javaに基づいて考えてください。

ありがとうございました。

15
Seregwethrin

Javaはストリームに書き込まれたオブジェクトを追跡し、後続のインスタンスは実際のシリアル化されたオブジェクトではなくIDとして書き込まれます。

したがって、例として、インスタンス「a」をストリームに書き込むと、ストリームはそのオブジェクトに一意のID(「1」としましょう)を与えます。 「a」のシリアル化の一環として、「b」をシリアル化する必要があり、ストリームはそれに別のID(「2」)を与えます。次に「b」をストリームに書き込む場合、書き込まれるのは実際のオブジェクトではなくIDのみです。

入力ストリームは同じことを逆に行います。ストリームから読み取る各オブジェクトに対して、出力ストリームと同じアルゴリズムを使用してID番号を割り当て、そのID番号はマップ内のオブジェクトインスタンスを参照します。 IDを使用してシリアル化されたオブジェクトを検出すると、マップから元のインスタンスを取得します。

これが API docs の説明です。

単一のオブジェクトへの複数の参照は、参照共有メカニズムを使用してエンコードされるため、オブジェクトのグラフは、元のオブジェクトが書き込まれたときと同じ形状に復元できます。

この動作により問題が発生する可能性があります。ストリームが各オブジェクトへのハード参照を保持しているため(IDをいつ置換するかがわかるため)、一時オブジェクトを大量にストリームに書き込むと、メモリが不足する可能性があります。これを解決するには、reset()を呼び出します。

25
parsifal

Javaでは、これは、シリアル化されたオブジェクトをキャッシュし、再度書き込まれたときにそのハンドルを書き込むことで解決されます。

http://docs.Oracle.com/javase/6/docs/platform/serialization/spec/output.html の手順5を参照してください。

2
ratchet freak