| 目次 | 前の項目 | 次の項目 | Java オブジェクト直列化仕様 Version 6.0 |
|
第 3 章 |
クラスObjectInputStreamは、オブジェクトの直列化復元を実装するためのものです。このクラスは、すでに直列化復元されたオブジェクトセットなどのストリームの状態を管理します。このクラスのメソッドを使えば、ObjectOutputStreamによって書き込まれたストリームから、プリミティブ型やオブジェクトを読み込むことができます。このクラスは、それが参照するオブジェクトをストリームから復元します。package java.io; public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants { public ObjectInputStream(InputStream in) throws StreamCorruptedException, IOException;public final Object readObject() throws OptionalDataException, ClassNotFoundException, IOException; public Object readUnshared() throws OptionalDataException, ClassNotFoundException, IOException; public void defaultReadObject() throws IOException, ClassNotFoundException, NotActiveException; public GetField readFields() throws IOException; public synchronized void registerValidation( ObjectInputValidation obj, int prio) throws NotActiveException, InvalidObjectException; protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException; protected Class resolveClass(ObjectStreamClass v) throws IOException, ClassNotFoundException; protected Object resolveObject(Object obj) throws IOException; protected boolean enableResolveObject(boolean enable) throws SecurityException; protected void readStreamHeader() throws IOException, StreamCorruptedException; public int read() throws IOException; public int read(byte[] data, int offset, int length) throws IOException public int available() throws IOException; public void close() throws IOException; public boolean readBoolean() throws IOException; public byte readByte() throws IOException; public int readUnsignedByte() throws IOException; public short readShort() throws IOException; public int readUnsignedShort() throws IOException; public char readChar() throws IOException; public int readInt() throws IOException; public long readLong() throws IOException; public float readFloat() throws IOException; public double readDouble() throws IOException; public void readFully(byte[] data) throws IOException; public void readFully(byte[] data, int offset, int size) throws IOException; public int skipBytes(int len) throws IOException; public String readLine() throws IOException; public String readUTF() throws IOException; // Class to provide access to serializable fields. static abstract public class GetField { public ObjectStreamClass getObjectStreamClass(); public boolean defaulted(String name) throws IOException, IllegalArgumentException; public char get(String name, char default) throws IOException, IllegalArgumentException; public boolean get(String name, boolean default) throws IOException, IllegalArgumentException; public byte get(String name, byte default) throws IOException, IllegalArgumentException; public short get(String name, short default) throws IOException, IllegalArgumentException; public int get(String name, int default) throws IOException, IllegalArgumentException; public long get(String name, long default) throws IOException, IllegalArgumentException; public float get(String name, float default) throws IOException, IllegalArgumentException; public double get(String name, double default) throws IOException, IllegalArgumentException; public Object get(String name, Object default) throws IOException, IllegalArgumentException; } protected ObjectInputStream() throws StreamCorruptedException, IOException; protected readObjectOverride() throws OptionalDataException, ClassNotFoundException, IOException; }単一引数のObjectInputStreamコンストラクタにはInputStreamが必要です。このコンストラクタは、readStreamHeaderを呼び出して読み込み、対応するObjectOutputStream.writeStreamHeaderメソッドによって書き込まれたヘッダーとバージョンを検査します。セキュリティーマネージャーがインストールされている場合、このコンストラクタは、readFieldsメソッドまたはreadUnsharedメソッド、あるいはその両方をオーバーライドするサブクラスのコンストラクタによって直接または間接的に呼び出されたときに、"enableSubclassImplementation"SerializablePermissionのチェックを行います。
注 -ObjectInputStreamコンストラクタは、直列化ストリームヘッダーの読み込みが完了するまでブロックされます。コードがObjectInputStreamの構築を待機しているときに、直列化ストリームに対して対応するObjectOutputStreamが作成されていない場合は、デッドロックが発生します。ObjectInputStreamコンストラクタは、ヘッダーがそのストリームに書き込まれるまでブロックされ、ヘッダーはObjectOutputStreamコンストラクタが実行されるまでストリームに書き込まれないためです。この問題は、ObjectInputStreamが構築される前にObjectOutputStreamを作成するか、ObjectInputStreamの構築の完了およびObjectOutputStreamの作成の間のタイミング依存関係を解除すると解決できます。
ストリームからオブジェクトを直列化復元するには、readObjectメソッドを使用します。このメソッドは、このストリームを読んで、オブジェクトを再構築します。
| 1. | ObjectInputStream サブクラスが実装をオーバーライドしている場合は、readObjectOverride メソッドを呼び出し、戻します。実装し直す方法については、この節の最後で説明します。 |
| 2. | ブロックデータレコードがストリームにあると、BlockDataException をスローし、使用可能なバイト数を知らせます。 |
| 3. | ストリームのオブジェクトが null であれば、null を返します。 |
| 4. | ストリームのオブジェクトが前のオブジェクトに対するハンドルであれば、そのオブジェクトを返します。 |
| 5. | ストリームのオブジェクトが Class であれば、その ObjectStreamClass 記述子を読み、それとそのハンドルを、認識されているオブジェクトセットに追加し、対応する Class オブジェクトを返します。 |
| 6. | ストリームのオブジェクトが ObjectStreamClass であれば、4.3 節で説明する書式に従ってそのデータを読みます。そのオブジェクトとそのハンドルを、認識されているオブジェクトセットに追加します。Version 1.3 以降の JavaTM 2 SDK, Standard Edition では、ObjectStreamClass がストリームデータで示されたダイナミックプロキシクラス以外のクラスを表す場合、readClassDescriptor メソッドが呼び出されてそのクラスを読み込みます。クラス記述子がダイナミックプロキシクラスを表すならば、ストリーム上で resolveProxyClass メソッドを呼び出し、その記述子のローカルクラスを取得します。 そうでない場合は、ストリーム上で resolveClass メソッドを呼び出し、ローカルクラスを取得します。クラスが解決できない場合は、ClassNotFoundException をスローします。取得した ObjectStreamClass オブジェクトを返します。 |
| 7. | ストリームのオブジェクトがStringの場合、その長さ情報を読み取ります。 その長さ情報のあとに、変更後の UTF-8 で符号化された文字列の内容が続きます。 詳細は、「6.2 ストリーム要素」を参照してください。String とそのハンドルを、認識されているオブジェクトセットに追加して、手順 12 に進みます。 |
| 8. | ストリームのオブジェクトが配列であれば、その ObjectStreamClass と配列の長さを読みます。その配列を割り当て、そのオブジェクトとそのハンドルを、認識されているオブジェクトセットに追加します。型に適したメソッドを使って各要素を読み込み、配列に割り当てます。手順 12 に進みます。 |
| 9. | ストリームのオブジェクトが enum 定数であれば、その ObjectStreamClass と enum 定数名を読みます。ObjectStreamClass が enum 型以外のクラスを表す場合、InvalidClassException がスローされます。受け取った ObjectStreamClass 名とともにバインドされた enum 型を引数として渡してjava.lang.Enum.valueOfメソッドを呼び出し、enum 定数への参照を取得します。valueOf メソッドで IllegalArgumentException がスローされる場合は、IllegalArgumentException が原因として InvalidObjectException がスローされます。enum 定数とそのハンドルを、認識されているオブジェクトセットに追加して、手順 12 に進みます。 |
| 10. | その他のオブジェクトの場合は、そのオブジェクトの ObjectStreamClass がストリームから読み込まれます。その ObjectStreamClass のローカルクラスが取り出されます。このクラスは、直列化可能か外部化可能でなければなりません。 また、enum 型以外でなければなりません。クラスがこの条件を満たさないと、InvalidClassException がスローされます。 |
| 11. | そのクラスのインスタンスが割り当てられます。そのインスタンスとそのハンドルが、認識されているオブジェクトセットに追加されます。内容が適切に復元されます。 |
| a. | 直列化可能オブジェクトの場合、最初の直列化不能なスーパータイプの引数なしのコンストラクタが実行されます。Serializable クラスの場合、フィールドは、その型に適したデフォルト値に初期化されます。次に、クラス固有の readObject メソッドか、これらが定義されていなければ、defaultReadObject メソッドが呼び出されて、各クラスのフィールドが復元されます。直列化復元時には、直列化可能クラスに対して、フィールドの初期化子およびコンストラクタは実行されません。通常、このストリームを書き込んだクラスのバージョンは、このストリームを読み込むクラスと同じです。この場合、ストリームのオブジェクトのすべてのスーパータイプは、現在ロードされているクラスのスーパータイプと一致します。このストリームを書き込んだクラスのバージョンのスーパータイプが、ロードされているクラスのスーパータイプと異なる場合は、ObjectInputStream で異なるクラスの状態を復元したり、初期化したりする際、いっそうの注意が必要です。この場合には、すべてのクラスを調べて、ストリームにあるデータと、復元するオブジェクトのクラスとを比較する必要があります。ストリームにはあるがオブジェクトにはないクラスのデータは破棄されます。オブジェクトにはあるがストリームにはないクラスの場合には、そのクラスのフィールドが、デフォルトの直列化によってデフォルト値に設定されます。
|
| b. | 外部化可能なオブジェクトの場合、そのクラスの引数なしのコンストラクタが実行されてから readExternal メソッドが呼び出され、そのオブジェクトの内容が復元されます。
|
| 12. | オブジェクトのクラス、または ObjectInputStream のサブクラスによって、置換を処理します。 |
| a. | オブジェクトのクラスが enum 型ではなく、適切な readResolve メソッドを定義する場合は、そのメソッドが呼び出され、オブジェクトが自らの置換を行うことを可能にします。
|
| b. | enableResolveObject によって resolveObject メソッドが使用可能にされていれば、resolveObject メソッドが呼び出されます。 これによって、ストリームのサブクラスがオブジェクトを調べ、それを置き換えることができます。前の手順で元のオブジェクトを置き換えた場合は、置換オブジェクトで resolveObject メソッドが呼び出されます。
|
置換が行われた場合、認識されているオブジェクトのテーブルが更新されるので、置換オブジェクトがハンドルに関連付けられます。そのあと、置換オブジェクトが readObject から返されます。 |
プリミティブ型を読むためのすべてのメソッドは、ストリームのブロックデータレコードからバイトだけを使用します。ストリームの次のアイテムがオブジェクトのときにプリミティブデータの読み込みが行われると、読み込みメソッドは -1 かEOFExceptionのうちで適切な方を返します。プリミティブ型の値は、DataInputStreamによってブロックデータレコードから読み込まれます。スローされた例外は、そのトラバースの間に起きたエラーか、基本のストリームで起きた例外を反映したものです。例外がスローされた場合、基本のストリームは不明で使用不能な状態のままです。
ストリームでリセットトークンが起こると、ストリームのすべての状態は破棄されます。認識されているオブジェクトセットはクリアされます。
ストリームで例外トークンが起こると、その例外が読み込まれ、新しい
WriteAbortedExceptionがスローされます。 このとき、停止を引き起こした例外が引数として指定されます。ストリームコンテキストは前に述べたようにリセットされます。ストリームから非共有オブジェクトを読み込むには、
readUnsharedメソッドを使用します。このメソッドは、後続のreadObjectおよびreadUnsharedの呼び出しが、元のreadUnsharedの呼び出しによって返された直列化復元インスタンスへの追加参照を返すことができない点を除いては、readObjectと同じです。具体的には、次のようになります。
readUnsharedを介してオブジェクトの直列化復元を行うと、返されたオブジェクトに関連付けられたストリームハンドルが無効になります。ただし、このことで、readUnsharedの返す参照が一意であることが常に保証されるわけではありません。直列化復元されたオブジェクトは、readResolveメソッドを定義して、ほかの組織が見ることができるオブジェクトを返すことがあります。 また、readUnsharedが、ストリーム内のほかの場所や外部機関から取得可能なClassオブジェクトまたは enum 定数を返すこともあります。直列化復元されたオブジェクトがreadResolveメソッドを定義し、そのメソッドの呼び出しにより配列が返される場合、readUnsharedはその配列のシャロー複製を返します。これにより、基本となるデータストリームが処理されている場合でも、返される配列オブジェクトが一意であり、ObjectInputStreamに対するreadObjectまたはreadUnsharedの呼び出しから 2 回目には取得できないようになります。ストリームからフィールドおよびオブジェクトを読み込むには、
defaultReadObjectメソッドを使用します。このメソッドは、ストリームのクラス記述子を使って、名前と型による標準の順序でストリームからそれらのフィールドを読み込みます。それらの値は、名前によって現行クラスの対応するフィールドに代入されます。バージョン管理機構の詳細については、「5.5 互換性のある TM の型展開」を参照してください。ストリーム内にないオブジェクトのフィールドは、そのデフォルト値に設定されます。ストリームにあるがオブジェクトにない値は、破棄されます。このような状態は主に、前のバージョンにはなかったフィールドを、クラスのあとのバージョンに追加で書き込んだ場合に起こります。このメソッドは、クラスのフィールドを復元している間にreadObjectメソッドからのみ呼び出すことができます。それ以外のときに呼び出すと、NotActiveExceptionがスローされます。
readFieldsメソッドは、ストリームから直列化可能フィールドの値を読み取り、GetFieldクラスを使ってその値を使用できるようにします。readFieldsメソッドは、直列化可能クラスのreadObjectメソッド内からしか呼び出すことができません。また、このメソッドは、1 回しか呼び出すことができず、defaultReadObjectがすでに呼び出されている場合は呼び出せません。GetFieldsオブジェクトは、現在のオブジェクトであるObjectStreamClassを使ってこのクラス用に取得できるフィールドを確認します。readFieldsによって返されるGetFieldsオブジェクトは、そのクラスのreadObjectメソッドへの呼び出しの間だけ有効です。フィールドは、任意の順序で取得できます。追加データの読み込みは、readFieldsが呼び出されたあとに、ストリームから直接読み込む場合だけ可能です。
registerValidationメソッドを呼び出すと、オブジェクトがreadObjectの元の呼び出し側に返される前にグラフ全体が復元された場合に、コールバックを要求することができます。有効化コールバックの順序は、優先順次で制御することができます。高い値のコールバックは、低い値のものより前に呼び出されます。有効にされるオブジェクトは、ObjectInputValidationインタフェースをサポートし、validateObjectメソッドを実装していなければなりません。有効化を登録するのは、クラスのreadObjectメソッドを呼び出す間でなければなりません。そうでないと、NotActiveExceptionがスローされます。registerValidationに指定されたコールバックオブジェクトが null の場合、InvalidObjectExceptionがスローされます。JavaTM SDK, Standard Edition, v1.3 から、すべての
ObjectStreamClassオブジェクトを読み込むときにreadClassDescriptorメソッドが使用されています。直列化ストリーム内でObjectInputStreamの次の項目がクラス記述子の場合は、readClassDescriptorが呼び出されます。writeClassDescriptorメソッドをオーバーライドしたObjectOutputStreamのサブクラスによって非標準形式で記述されたクラス記述子が読み込むためのこのメソッドは、ObjectInputStreamのサブクラスによってオーバーライドされます。デフォルトでは、このメソッドは、「6.4 ストリーム形式の文法」で説明している形式に従ってクラス記述子を読み込みます。
resolveClassメソッドは、直列化復元されている間と、そのクラス記述子が読み込まれたあとで呼び出されます。サブクラスは、このメソッドを拡張して、ObjectOutputStreamの対応するサブクラスによって書き込まれたクラスの他の情報を読むことができます。このメソッドは、与えられた名前とserialVersionUIDを持つクラスを見つけ、返さなければなりません。デフォルトの実装では、このクラスは、クラスローダを持つreadObjectのもっとも近い呼び出し側のクラスローダを呼び出すことによって、見つけることができます。このクラスが見つからないと、ClassNotFoundExceptionがスローされます。JDKTM 1.1.6 より前のバージョンでは、resolveClassメソッドは、ストリーム内のクラス名と同じ、完全指定されたクラス名を返す必要がありました。JDKTM 1.1.6 以降のバージョンでは、リリースごとにパッケージの名前を変更できるようにするために、resolveClassメソッドは、同じ基底クラス名およびSerialVersionUIDを持つクラスを返すことだけが必要です。
resolveObjectメソッドは、直列化復元の際に、あるオブジェクトを別のオブジェクトの代わりにモニターしたり、あるオブジェクトと別のオブジェクトを置換したりするために、信頼できるサブクラスによって使用されます。解釈処理する最初のオブジェクトに対してreadObjectを呼び出す前に、enableResolveObjectを呼び出して、オブジェクトの解析処理を明示的に使用可能にしなければなりません。オブジェクトの解析処理を使用可能にすると、それぞれの直列化可能オブジェクトがreadObjectから最初に返される直前に、resolveObjectは一度だけ呼び出されます。resolveObjectメソッドは、特別に処理されるクラスであるClass、ObjectStreamClass、String、および配列のオブジェクトに対しては呼び出されません。サブクラスのresolveObjectの実装では、オリジナルの代わりに代入されたり返されたりする置換オブジェクトが返される場合があります。返されるオブジェクトは、一貫性があり、元のオブジェクトの参照に必ず代入できる型のものでなければなりません。 そうでないと、ClassCastExceptionがスローされます。すべての代入では型の検査が行われます。ストリームにおける元のオブジェクトへのすべての参照は、置換オブジェクトへの参照によって置き換えられます。
enableResolveObjectメソッドは、直列化復元の際に、あるオブジェクトを別のオブジェクトの代わりにモニターしたり、あるオブジェクトで別のオブジェクトを置換することを可能にするために、ObjectOutputStreamの信頼できるサブクラスによって呼び出されます。オブジェクトの置換は、enableResolveObjectがtrue値で呼び出されるまでは、使用不可になっています。また、使用可能にしたあとで、falseに設定して、使用不可にされる場合があります。前の設定が返されます。enableResolveObjectメソッドは、直列化の際にストリームが置換を要求する権限があるかどうかを検査します。オブジェクトの private 状態が意図せずに変更されることのないように、信頼できるストリームだけしかresolveObjectを使用することはできません。信頼できるクラスとは、クラスローダが null に等しいか、置換を有効にすることを許可するセキュリティー保護ドメインに属するクラスのことです。
ObjectInputStreamのサブクラスがシステムドメインの一部でないとみなされる場合は、enableResolveObjectを呼び出すための許可をObjectInputStreamのサブクラスに与える行を、セキュリティーポリシーファイルに追加する必要があります。追加するSerializablePermissionは、"enableSubstitution"です。ObjectStreamClassのサブクラスの保護ドメインに、enableResolveObjectの呼び出しによる"enableSubstitution"の権限がない場合は、AccessControlExceptionがスローされます。セキュリティーモデルの詳細は、JavaTM セキュリティーアーキテクチャー (JDKTM 1.2) のドキュメントを参照してください。
readStreamHeaderメソッドは、ストリームのマジック番号とバージョンを読み込み、検査します。それらが一致しないと、StreamCorruptedMismatchがスローされます。直列化復元の実装をオーバーライドするには、
ObjectInputStreamのサブクラスは、保護された引数なしのObjectInputStreamコンストラクタを呼び出す必要があります。SerializablePermission "enableSubclassImplementation"の引数なしのコンストラクタ内にはセキュリティーチェックがあり、信頼できるクラスだけにデフォルトの実装のオーバーライドを許可します。このコンストラクタは、ObjectInputStreamに private なデータを割り当てず、ファイナルのreadObjectメソッドはreadObjectOverrideメソッドを呼び出してから復帰することを示すフラグを設定します。ほかのすべてのObjectInputStreamメソッドは、ファイナルではないので、サブクラスによって直接オーバーライドされます。
ObjectInputStream.GetFieldは、直列化可能フィールドの値を取得するための API を提供します。ストリームのプロトコルは、defaultReadObjectが使うプロトコルと同じです。readFieldsの使用による直列化可能フィールドへのアクセスでは、ストリームの形式は変更されずに、値にアクセスするための代わりの API が提供されるだけです。この API は、クラスの各名前付き直列化可能フィールドに対し、対応する非 transient および非 static フィールドを必要としません。直列化可能フィールドは、serialPersistentFieldsを使って宣言されたフィールドであるか、あるいはそのようなフィールドが宣言されていない場合は、オブジェクトの非 transient および非 static フィールドです。ストリームの読み込み時に利用できる直列化可能フィールドは、オブジェクトの直列化時にストリームに書き込まれたフィールドです。ストリームを書き込むクラスのバージョンが異なる場合は、すべてのフィールドが現在のクラスの直列化可能フィールドに対応するわけではありません。利用可能なフィールドは、GetFieldオブジェクトのObjectStreamClassから取得できます。
getObjectStreamClassメソッドは、ストリーム内のクラスを表すObjectStreamClassオブジェクトを返します。これには、直列化可能フィールドの一覧が含まれています。ストリーム内にフィールドが存在しない場合は、
defaultedメソッドが true を返します。要求されたフィールドが現在のクラスの直列化可能フィールドでない場合は、IllegalArgumentExceptionがスローされます。各
getメソッドは、指定された直列化可能フィールドをストリームから返します。背後のストリームが例外をスローした場合は、入出力例外がスローされます。現在のクラスの直列化可能フィールドの名前および型に、名前または型が一致しない場合は、IllegalArgumentExceptionがスローされます。ストリームのそのフィールドに明示的な値が含まれていない場合は、デフォルトの値が返されます。
このインタフェースを使うと、オブジェクトの完全グラフ (オブジェクトが構成する完全グラフ) が直列化復元されたとき、オブジェクトが呼び出されます。オブジェクトを有効にできない場合、このインタフェースは通常ObjectInvalidExceptionをスローします。validateObjectの呼び出しで例外が起こると、有効化処理は中止され、InvalidObjectExceptionがスローされます。package java.io; public interface ObjectInputValidation { public void validateObject() throws InvalidObjectException; }
Serializable オブジェクトの場合、readObjectメソッドによって、クラスがそれ独自のフィールドの直列化復元を制御することができます。そのシグニチャーを次に示します。private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException;Serializable オブジェクトの各サブクラスは、それ独自のreadObjectメソッドを定義することができます。このメソッドがクラスに実装されていない場合は、defaultReadObjectによって与えられるデフォルトの直列化が使用されます。実装されている場合は、そのクラスは、そのスーパータイプやサブタイプのフィールドではなく、それ独自のフィールドだけを復元する責任があります。クラスの
readObjectメソッドが実装されていれば、そのメソッドはそのクラスの状態を復元する責任があります。transient または static かどうかには関係なく、オブジェクトのすべてのフィールドの値に、そのフィールドタイプのデフォルトの値が設定されます。ObjectInputStreamのdefaultReadObjectメソッドまたはreadFieldsメソッドを一度 (一度だけ) 呼び出してからでないと、対応するwriteObjectメソッドによって書き込まれた任意指定のデータを読み込むことはできません。任意指定のデータを読み込まない場合でも、defaultReadObjectまたはreadFieldsを一度呼び出す必要があります。クラスのreadObjectメソッドが、このクラスのストリーム内に存在する任意指定部分よりも多くのデータを読み込もうとすると、バイトの読み込みでは-1が返され、プリミティブデータの読み込み (readInt、readFloatなど) ではEOFExceptionがスローされ、オブジェクトの読み込みではeofフィールドがtrueに設定されたOptionalDataExceptionがスローされます。この任意指定データの形式、構造体、バージョン管理の責任のすべては、そのクラスにあります。任意指定データの形式および構造を文書化する場合は、
readObjectメソッドに対する javadoc コメント内の@serialDatajavadoc タグを使う必要があります。復元するクラスが読み込みストリーム内に存在しない場合、
readObjectNoDataメソッドが定義されていれば、(readObjectの代わりに) そのメソッドが呼び出され、定義されていなければ、フィールドは適切なデフォルト値に初期化されます。詳細は、「3.5 readObjectNoData メソッド」 を参照してください。
ObjectInputStreamからのオブジェクトの読み込みは、新しいオブジェクトの作成に似ています。新しいオブジェクトのコンストラクタがスーパークラスからサブクラスという順番で呼び出されるのと同様に、ストリームから読み込まれるオブジェクトは、スーパークラスからサブクラスに直列化復元されます。直列化復元中に、各Serializableサブクラスに対して、コンストラクタではなくreadObjectまたはreadObjectNoDataメソッドが呼び出されます。コンストラクタと
readObjectメソッドでもう 1 つ似ている点は、どちらも、完全に構築されていないオブジェクト上にメソッドを呼び出すことができる点です。オブジェクトの構築中に呼び出されるオーバーライド可能なメソッド (private、static、final のどれでもないもの) は、サブクラスによってオーバーライドされる可能性があります。オブジェクトの構築段階に呼び出されるメソッドは、コンストラクタかreadObjectまたはreadObjectNoDataメソッドによって現在初期化されている型ではなく、そのオブジェクトの実際の型によって解釈処理されます。したがって、readObjectまたはreadObjectNoDataメソッド内からオーバーライド可能なメソッドを呼び出すと、スーパークラスが完全に初期化される前に意図しないサブクラスが呼び出される可能性があります。
直列化可能オブジェクトでは、サブクラスのインスタンスが直列化復元され、直列化ストリームがそのクラスを直列化復元されたオブジェクトのスーパークラスとしてリストしない場合、readObjectNoDataメソッドを使うと、クラスが自らのフィールドの初期化を制御することができます。これは、受け取り側が、送り側とは異なるバージョンの直列化復元されたインスタンスのクラスを使用し、受け取り側のバージョンが、送り側のバージョンによって継承されないクラスを継承する場合に発生する可能性があります。また、直列化ストリームが改変された場合にも発生することがあります。したがって、readObjectNoDataは、「悪意のある」または不正なソースストリームであっても、直列化復元されたオブジェクトを正しく初期化するのに役立ちます。private void readObjectNoData() throws ObjectStreamException;直列化可能クラスごとに、そのクラス独自のreadObjectNoDataメソッドを定義することができます。直列化可能クラスがreadObjectNoDataメソッドを定義しない場合、上記の状況では、クラスのフィールドはデフォルト値 (「JavaTM Language Specification, Second Edition」の 4.5.5 に記載) に初期化されます。この動作は、readObjectNoDataメソッドのサポートが導入された JavaTM 2 SDK, Standard Edition Version 1.4 以前のObjectInputStreamの動作と一致します。直列化可能クラスがreadObjectNoDataメソッドを定義する場合に前述の状況が発生すると、クラス定義のreadObjectメソッドが呼び出されたときに、そのクラスがストリームによって直列化復元するインスタンスのスーパークラスとしてリストされていれば、直列化復元中のその時点でreadObjectNoDataが呼び出されます。
java.io.Externalizableを実装するオブジェクトは、readExternalメソッドを実装して、そのオブジェクトの状態全体を復元しなければなりません。このオブジェクトは、そのスーパークラスと連携してそれらの状態を復元する必要があります。ObjectInputのすべてのメソッドが、そのオブジェクトのプリミティブ型のフィールドとオブジェクトフィールドを復元するために使用できます。public void readExternal(ObjectInput stream) throws IOException;
注 -readExternalメソッドは public であるため、クライアントがストリームの既存オブジェクトを上書きしてしまう危険性があります。クラスで独自にチェックを追加して、適切なときにだけ呼び出されるようにすることもできます。
JDKTM 1.2 では、Externalizableオブジェクトの問題を修正するために、新しいストリームプロトコルのバージョンが導入されました。Externalizableオブジェクトの以前の定義では、ストリームからExternalizableオブジェクトを適切に読み込めるようにするために、ローカルの仮想マシンがreadExternalメソッドを探す必要がありました。新しい形式では、十分な情報がストリームプロトコルに追加されるので、ローカルのreadExternalメソッドが使えない場合は、直列化でExternalizableオブジェクトをスキップすることができます。クラスの展開規則により、ローカルクラスを使ったオブジェクトのマッピングがない場合は、入力ストリーム内のExternalizableオブジェクトはスキップできます。また、新しい
Externalizableストリーム形式では、ObjectInputStreamによって、利用可能な範囲を超える外部データの読み込み試行を検出し、readExternalメソッドによって消費されていないデータをスキップできるという利点もあります。外部データの終端を過ぎた読み取りに応答するObjectInputStreamの動作は、クラス定義のreadObjectメソッドが任意指定データの終端を超えて読み込もうとしたときの動作と同じです。バイトの読み込みでは-1を返し、プリミティブの読み込みではEOFExceptionをスローし、オブジェクトの読み込みでは、eofフィールドをtrueに設定したOptionalDataExceptionをスローします。この形式の変更のため、JDKTM 1.1.6 以前のリリースでは、この新しい形式を読み込むことができません。JDKTM 1.1.6 以前で、
PROTOCOL_VERSION_2に書き込まれたストリームからExternalizableオブジェクトを読み込もうとすると、StreamCorruptedExceptionがスローされます。互換性の問題については、「6.3 ストリームプロトコルのバージョン」を参照してください。
Serializable クラスと Externalizable クラスの場合、クラスはreadResolveメソッドを使うことによって、呼び出し側に返される前に、ストリームから読み込んだオブジェクトを置換または解釈処理できます。readResolveメソッドを実装することによって、クラスは、クラス自体の直列化復元されているインスタンスの型およびインスタンスを直接制御できます。このメソッドは、次のように定義します。ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;readResolveメソッドは、ObjectInputStreamがストリームからオブジェクトを読み込み、呼び出し側に返す準備をしているときに呼び出されます。ObjectInputStreamは、オブジェクトのクラスによってreadResolveメソッドが定義されるかどうかを確認します。このメソッドが定義される場合は、readResolveメソッドが呼び出されて、ストリーム内のオブジェクトは、オブジェクトが返されるよう指定できるようになります。返されるオブジェクトは、すべての使用場面で互換性がある型でなければなりません。互換性がない場合は、型の不一致が発見された時点でClassCastExceptionがスローされます。たとえば、
Symbolクラスは、仮想マシン内で各シンボルバインディングにインスタンスが 1 つだけ存在するSymbolクラスを生成することもできます。そのシンボルがすでに定義されているかどうか、およびアイデンティティーの制約を守るために、以前から存在するSymbolオブジェクトをそのシンボルに取り替えるかどうかを決定するreadResolveメソッドを実装します。このようにして、直列化におけるSymbolオブジェクトの一意性を守ることができます。
注 - オブジェクトが完全に構築されるまではreadResolveメソッドはオブジェクトに呼び出されないため、そのオブジェクトのオブジェクトグラフへの参照は、readResolveによって指定された新しいオブジェクトに更新されません。しかし、writeReplaceメソッドによるオブジェクトの直列化の間に、置換オブジェクトのオブジェクトグラフにある元のオブジェクトへの参照はすべて、置換オブジェクトへの参照に置き換えられます。したがって、直列化されたオブジェクトが置換オブジェクトを指定し、その置換オブジェクトのオブジェクトグラフが元のオブジェクトへの参照を持つ場合、直列化復元を行うと、不正なオブジェクトグラフが作成されます。さらに、writeReplaceによって指定された読み込み対象のオブジェクトと元のオブジェクトの参照タイプに互換性がない場合、オブジェクトグラフを構築する際にClassCastExceptionが発生します。