Serializable
を実装します。
オブジェクト・ストリーム・クラスは、ObjectInputStream
とObjectOutputStream
です。 これらのクラスは、DataInput
のサブインタフェースであるObjectInput
か、DataOutput
のサブインタフェースであるObjectOutput
を実装しています。 つまり、データ・ストリームで扱われるプリミティブ・データのI/Oメソッドはすべて、オブジェクト・ストリームでも実装されています。 このため、オブジェクト・ストリームには、プリミティブ値とオブジェクト値を同時に含めることができます。 このことは、サンプル・プログラムのObjectStreams
で確認できます。 ObjectStreams
で作成されるアプリケーションは、前述のサンプル・プログラムDataStreams
で作成されるものと基本的には同じですが、いくつか変更点があります。 まず、価格には、小数値をより適切に表すBigDecimal
オブジェクトを使用しています。 また、請求書の日付を表すCalendar
オブジェクトを、データ・ファイルに書き込んでいます。
readObject()
で返されるオブジェクトの型が期待したものと異なる場合、正しい型にキャストしようとするとClassNotFoundException
がスローされることがあります。 この単純なサンプル・プログラムではこうした事態は発生しないため、例外のキャッチはしていません。 代わりに、main
メソッドのthrows
句にClassNotFoundException
を追加することで、このような問題を認識していることをコンパイラに通知しています。
writeObject
メソッドとreadObject
メソッドは、簡単に使用できる一方で、ある非常に高度なオブジェクト管理ロジックを含んでいます。 このことは、Calendarのような、単にプリミティブ値をカプセル化するだけのクラスには重要ではありません。 しかし、多くのオブジェクトには他のオブジェクトへの参照が含まれています。 readObject
がストリームからオブジェクトを再構成する場合、元のオブジェクトが参照していたすべてのオブジェクトを再構成できる必要があります。 さらに、これらのオブジェクトもまた別のオブジェクトを参照している可能性があり、こうした参照関係が延々と続く場合もあります。 このような状況において、writeObject
は網状に広がったオブジェクト参照構造の全体をトラバースして、その構造内のすべてのオブジェクトをストリームに書き込みます。 したがって、writeObject
を1回呼び出しただけで、大量のオブジェクトがストリームに書き込まれる場合があります。
この状況を、次の図で示します。ここでは、aという名前の1つのオブジェクトを書き込むために、writeObject
が呼び出されます。 このオブジェクトには、bおよびcというオブジェクトへの参照が含まれ、さらにbにはdおよびeへの参照が含まれます。 writeobject(a)
を呼び出すと、aだけではなく、aの再構成に必要なすべてのオブジェクトも書き込まれます。このため、この網状の構造内にある他の4つのオブジェクトも書き込まれることになります。 aがreadObject
によって再度読み取られると、他の4つのオブジェクトも同様に読み取られ、元のオブジェクト参照のすべてが維持されます。
複数の参照先オブジェクトのI/O
ob
というオブジェクトをストリームに2回書き込んでいます。
Object ob = new Object(); out.writeObject(ob); out.writeObject(ob);
writeObject
はreadObject
に対応させる必要があるので、このストリームを再度読み取るコードは次のようになります。
Object ob1 = in.readObject(); Object ob2 = in.readObject();
ob1
とob2
という2つの変数は、同じ1つのオブジェクトへの参照となります。
ただし、1つのオブジェクトが別々の2つのストリームに書き込まれる場合は、そのオブジェクトは実際に複製されます。つまり、1つのプログラムで両方のストリームを再度読み取ると、2つの異なるオブジェクトを確認できます。