0

クライアント/サーバープログラムがあり、ObjectOutputStream経由writeObject()してオブジェクトを送信していますreadObject()

私が送信しているオブジェクトは、いくつかのフィールドと内部の別のオブジェクトで構成されるクラスです(外部オブジェクトWrapperと内部オブジェクトを呼び出しましょうInner。両方のカスタムオブジェクトはを実装しserializableます。

初めて送信するときはWrapper、すべてが問題なく機能します。両方に格納されているすべてのものWrapperInnerおよびすべてのフィールドは、問題なくシリアル化および逆シリアル化されます。

ただし、クライアントがInnerクラスを変更し、それをに入れてWrapperもう一度送信するとInner、サーバーが受信するインスタンスは、最初に受信したインスタンスと同じになります。

私の顧客:

Inner inner = new Inner();
inner.setValue("value");

ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(new Wrapper(inner));

サーバ:

ObjectInputStream in = new ObjectInputStream(clientSocket.getInputStream());
Wrapper wrapper = (Wrapper) in.readObject();
String value = wrapper.getInner().getValue();

次に、クライアントはInnerクラス(以前と同じインスタンス)をDIFFERENT文字列(つまり、最初の文字以外の文字を含む)で変更します。

inner.setValue("newValue");
out.writeObject(new Wrapper(inner));

ただし、inner.getValue()サーバー上でを見ると、変更されておらず、まだに等しい"value"です。

送信する前に、内部クラスのハードコピーを作成することで、これを解決しました。

Inner newInner = new Inner();
newInner.setValue("newValue");
out.writeObject(new Wrapper(newInner));

新しい値が更新されます。

シリアル化がこのように機能するのはなぜですか?

4

1 に答える 1

2

これは、 の予想される動作ですObjectOutputStream。Javadocs から引用するには:

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

同じ内部クラス参照を使用しているため、以前に送信されたオブジェクトへの参照を送信するだけです。すべてのオブジェクトのすべてのフィールドをチェックして、それらのいずれかが変更されているかどうかを確認するわけではありません。inner.getValue()が最初に送信されたオブジェクトと等しいだけでなくinner、2 番目のオブジェクトでサーバーが受信したオブジェクトが、最初のオブジェクトからのものと同じオブジェクト( ==) であったと思われinnerます。

電話した場合:

out.reset();

調整されたフィールドを持つオブジェクトを送信する前に、innerコードが機能するはずです。このreset()メソッドは、シリアライゼーション ストリームを効率的にするのに役立つ参照キャッシュをクリアします。補足として、ストリームを介して多数の一時オブジェクトをreset()送信する場合は特に必要です。そうしないと、これらのオブジェクトがメモリにキャッシュされ、ヒープが枯渇する可能性があるためです。

于 2013-02-14T20:10:15.047 に答える