| 目次 | 前へ | 次へ | 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 以降の Java 2 SDK, Standard Edition では、readClassDescriptor がストリームデータで示された動的プロキシクラス以外のクラスを表す場合、読み取るために ObjectStreamClass 内で readClassDescriptor メソッドが呼び出されます。クラス記述子が動的プロキシクラスを表す場合は、記述子のローカルクラスを取得するためにストリーム上で resolveProxyClass メソッドを呼び出します。そうでない場合は、ローカルクラスを取得するためにストリーム上で resolveClass メソッドを呼び出します。クラスが解決できない場合は、ClassNotFoundException をスローします。結果として得られる ObjectStreamClass オブジェクトを返します。 |
| 7. | ストリーム内のオブジェクトが String の場合、その長さ情報と modified 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. | 直列化可能オブジェクトの場合、最初の非直列化可能スーパータイプの引数なしコンストラクタが実行されます。直列化可能クラスの場合、その型に適したデフォルト値にフィールドが初期化されます。次に、クラス固有の readObject メソッドか、これらが定義されていない場合は defaultReadObject メソッドが呼び出されて、各クラスのフィールドが復元されます。直列化復元時には、直列化可能クラスのフィールドイニシャライザおよびコンストラクタは実行されません。通常、このストリームを書き込んだクラスのバージョンは、ストリームを読み込むクラスと同じです。この場合、ストリーム内のオブジェクトのすべてのスーパータイプは、現在ロードされているクラスのスーパータイプと一致します。ストリームを書き込んだクラスのバージョンのスーパータイプが、ロードされているクラスのスーパータイプと異なる場合は、ObjectInputStream で異なるクラスの状態を復元または初期化する際に一層の注意が必要です。それらのクラスについて、ストリーム内で利用可能なデータと、復元するオブジェクトのクラスとを照合する必要があります。ストリームにはあるがオブジェクトにはないクラスのデータは破棄されます。オブジェクトにはあるがストリームにはないクラスの場合には、クラスフィールドはデフォルト直列化によってデフォルト値に設定されます。
|
| b. | Externalizable オブジェクトの場合、クラスの引数なしコンストラクタが実行されてから、オブジェクトの内容を復元するために readExternal メソッドが呼び出されます。
|
| 12. | オブジェクトのクラスまたは ObjectInputStream: のサブクラス (あるいはその両方) による潜在的な置換を処理します。 |
| a. | オブジェクトのクラスが enum 型ではなく、適切な readResolve メソッドを定義している場合は、オブジェクトが自身を置換するためにそのメソッドが呼び出されます。
|
| b. | そして、すでに enableResolveObject によって使用可能にされている場合は、ストリームのサブクラスがオブジェクトを調べて置換するために resolveObject メソッドが呼び出されます。前の手順で元のオブジェクトを置き換えた場合は、置換オブジェクトで resolveObject メソッドが呼び出されます。
|
置換が行われると、既知のオブジェクトのテーブルが更新されて、置換オブジェクトがハンドルに関連付けられます。置換オブジェクトが readObject から返されます。 |
プリミティブ型を読む取るためのすべてのメソッドは、ストリームのブロックデータレコードからのバイトのみを消費します。ストリーム内の次のアイテムがオブジェクトのときにプリミティブデータの読み込みが行われると、読み込みメソッドは -1 か EOFException のうちで適切な方を返します。プリミティブ型の値は、DataInputStream によってブロックデータレコードから読み込まれます。
ストリームでリセットトークンが起こると、ストリームのすべての状態は破棄されます。既知のオブジェクトのセットはクリアされます。
ストリームで例外トークンが起こると、例外が読み込まれ、終了させた例外を引数として新しい WriteAbortedException がスローされます。ストリームコンテキストは前に述べたようにリセットされます。
ストリームから非共有オブジェクトを読み込むには、readUnshared メソッドを使用します。このメソッドは readObject と同じですが、後続の readObject および readUnshared の呼び出しが、元の readUnshared の呼び出しによって返される直列化復元インスタンスへの追加参照を返すことができない点が異なります。具体的には、次のようになります。
readUnshared を呼び出して逆参照 (以前にストリームに書き込まれたオブジェクトのストリーム表現) を直列化復元しようとすると、ObjectStreamException がスローされます。
readUnshared が正常に復帰したあとで、readUnshared が直列化復元したストリームハンドルへの逆参照を直列化復元しようとすると、ObjectStreamException がスローされます。
readUnshared でオブジェクトを直列化復元すると、返されるオブジェクトに関連付けられたストリームハンドルが無効になります。ただし、これ自体が readUnshared から返される参照が一意であることを保証するとは限りません。直列化復元されたオブジェクトが、ほかの関係者が見ることができるオブジェクトを返したり、readUnshared がストリーム内のどこかでまたは外部から取得できる Class オブジェクトまたは enum 定数を返したりする、readResolve メソッドを定義している可能性があります。直列化復元されたオブジェクトが readResolve メソッドを定義し、このメソッドの呼び出しが配列を返す場合、readUnshared はその配列のシャロークローンを返します。これにより、基になるデータストリームが操作された場合でも、返される配列オブジェクトが一意であり、ObjectInputStream 上の readObject または readUnshared 呼び出しから 2 回取得できないことが保証されます。
ストリームからフィールドおよびオブジェクトを読み込むには、defaultReadObject メソッドを使用します。このメソッドは、ストリーム内のクラス記述子を使って、名前と型による正規順序でストリームからフィールドを読み込みます。値は、現在のクラス内の名前で一致するフィールドに代入されます。バージョン管理メカニズムの詳細については、セクション 5.5「互換性のある Java の型展開」を参照してください。ストリーム内にないオブジェクトのフィールドは、そのデフォルト値に設定されます。ストリームにあるがオブジェクトにない値は、破棄されます。このような状態は主に、前のバージョンにはなかったフィールドを、クラスのあとのバージョンに追加で書き込んだ場合に起こります。このメソッドは、クラスのフィールドを復元している間に readObject メソッドからのみ呼び出すことができます。それ以外のときに呼び出すと、NotActiveException がスローされます。
readFields メソッドは、ストリームから直列化可能フィールドの値を読み取り、GetField クラスでその値を取得できるようにします。readFields メソッドは、直列化可能クラスの readObject メソッド内からしか呼び出すことができません。このメソッドは、1 回より多く、または defaultReadObject が呼び出されている場合に、呼び出せません。GetFields オブジェクトは、現在のオブジェクトの ObjectStreamClass を使って、このクラスのために取得できるフィールドを確認します。readFields によって返される GetFields オブジェクトは、クラス readObject メソッドへのこの呼び出しの間だけ有効です。フィールドは、任意の順序で取得できます。追加データの読み込みは、readFields が呼び出されたあとに、ストリームから直接読み込む場合だけ可能です。
registerValidation メソッドは、グラフ全体が復元されたけれどもオブジェクトが readObject の元の呼び出し側に返される前のときに、コールバックを要求するために呼び出すことができます。検証コールバックの順序は、優先順位で制御できます。高い値のコールバックは、低い値のものより前に呼び出されます。検証されるオブジェクトは、ObjectInputValidation インタフェースをサポートし、validateObject メソッドを実装しなければいけません。クラスの readObject メソッド呼び出し中に検証を登録することのみが正しいです。そうでない場合は、NotActiveException がスローされます。registerValidation に指定されたコールバックオブジェクトが null の場合、InvalidObjectException がスローされます。
Java SDK, Standard Edition, v1.3 から、すべての ObjectStreamClass オブジェクトを読み込むには readClassDescriptor メソッドが使用されています。直列化ストリーム内で ObjectInputStream が次の項目としてクラス記述子を期待する場合は、readClassDescriptor が呼び出されます。非標準の形式で (writeClassDescriptor メソッドをオーバーライドした ObjectOutputStream のサブクラスによって) 記述されたクラス記述子内で読み取るために、ObjectInputStream のサブクラスがこのメソッドをオーバーライドできます。デフォルトでは、このメソッドは、セクション 6.4「ストリーム形式の文法」で説明している形式に従ってクラス記述子を読み込みます。
resolveClass メソッドは、クラスが直列化復元されている間、かつクラス記述子が読み込まれたあとで呼び出されます。サブクラスは、ObjectOutputStream の対応するサブクラスで記述された、クラスに関するほかの情報を読み取るために、このメソッドを拡張できます。メソッドは、指定された名前と serialVersionUID を持つクラスを見つけて返す必要があります。デフォルト実装は、クラスローダを持つ readObject のもっとも近い呼び出し側のクラスローダを呼び出すことによって、クラスを見つけます。クラスが見つからない場合は、ClassNotFoundException がスローされるはずです。JDK 1.1.6 より前では、resolveClass メソッドは、ストリーム内のクラス名と同じ完全修飾クラス名を返す必要がありました。JDK 1.1.6 以降のバージョンでは、リリースをまたがったパッケージ名変更に対応するために、method resolveClass に必要なのは、同じ基底クラス名と SerialVersionUID を持つクラスを返すことだけです。
resolveObject メソッドは、直列化復元の際に、あるオブジェクトを監視したり別のオブジェクトに置換したりするために、信頼できるサブクラスによって使用されます。最初のオブジェクトを解決するには、readObject を呼び出す前に enableResolveObject を呼び出すことでオブジェクトの解決を明示的に有効にする必要があります。有効になった resolveObject は、各直列化可能オブジェクトが readObject から最初に返される直前に、一度だけ呼び出されます。resolveObject メソッドは、特別に処理されるクラス Class、ObjectStreamClass、String、および配列のオブジェクトの場合は呼び出されません。サブクラスの resolveObject の実装は、オリジナルの代わりに代入または返される置換オブジェクトを返す場合があります。返されるオブジェクトは、元のオブジェクトのすべての参照と一貫性を持ちそれらに代入可能な型である必要があり、そうでない場合は ClassCastException がスローされます。すべての代入は型チェックされます。ストリーム内の、元のオブジェクトへのすべての参照は、置換オブジェクトへの参照によって置き換えられます。
enableResolveObject メソッドは、直列化復元の際に、あるオブジェクトを監視したり別のオブジェクトに置換したりするために、ObjectOutputStream の信頼できるサブクラスによって呼び出されます。オブジェクトの置換は、enableResolveObject が true 値で呼び出されるまで無効になっています。それ以降は、false に設定することで無効にできます。前の設定が返されます。enableResolveObject メソッドは、直列化の際にストリームが置換を要求する権限を持つかどうかを検査します。オブジェクトの private 状態が意図せずに公開されることがないように、信頼できるストリームだけが resolveObject を使用できます。信頼できるクラスとは、クラスローダが null に等しいか、置換を有効にする権限を提供するセキュリティー保護ドメインに属するクラスのことです。
ObjectInputStream のサブクラスがシステムドメインの一部とみなされない場合は、enableResolveObject を呼び出す権限を ObjectInputStream のサブクラスに提供する行をセキュリティーポリシーファイルに追加する必要があります。追加する SerializablePermission は、"enableSubstitution" です。ObjectStreamClass のサブクラスの保護ドメインが enableResolveObject を呼び出して "enableSubstitution" する権限を持たない場合は、AccessControlException がスローされます。セキュリティーモデルの詳細は、Java セキュリティーアーキテクチャー (JDKTM 1.2) のドキュメントを参照してください。
readStreamHeader メソッドは、ストリームのマジック番号とバージョンを読み込み、検査します。それらが一致しない場合、StreamCorruptedMismatch がスローされます。
直列化復元の実装をオーバーライドするには、ObjectInputStream のサブクラスが protected 引数なし ObjectInputStream コンストラクタを呼び出す必要があります。SerializablePermission "enableSubclassImplementation" 用の引数なしコンストラクタ内では、信頼できるクラスだけがデフォルト実装のオーバーライドを許可されるようにセキュリティーチェックがあります。このコンストラクタは、ObjectInputStream に private データを割り当てず、final readObject メソッドが readObjectOverride メソッドを呼び出して復帰すべきことを示すフラグを設定します。ほかのすべての ObjectInputStream メソッドは、final ではないので、サブクラスによって直接オーバーライドできます。
ObjectInputStream.GetField は、直列化可能フィールドの値を取得するための API を提供します。ストリームのプロトコルは、defaultReadObject. によって使用されるものと同じです。readFields を使用して直列化可能フィールドにアクセスしても、ストリームのフォーマットは変わりません。それらの値にアクセスするための代替 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;
}
readObject メソッドによって独自のフィールドの直列化復元を制御できます。そのシグニチャーを次に示します。 private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException;
直列化可能オブジェクトの各サブクラスは、独自の readObject メソッドを定義できます。クラスがメソッドを実装しない場合は、defaultReadObject によって提供されるデフォルト直列化が使用されます。実装されたクラスは、そのスーパータイプまたはサブタイプのフィールドではなく、独自のフィールドの復元だけに責任を持ちます。
クラスの readObject メソッドは (実装されている場合)、そのクラスの状態を復元する責任を持ちます。transient かどうか static かどうかには関係なく、オブジェクトの各フィールドの値は、フィールド型のデフォルト値に設定されます。ObjectInputStream の defaultReadObject または readFields メソッドは、対応する writeObject メソッドによって書き出されたオプションデータを読み取る前に、一度 (一度だけ) 呼び出す必要があります。オプションデータを読み取らない場合でも、defaultReadObject または readFields を一度呼び出す必要があります。クラスの readObject メソッドが、このクラスのストリームのオプション部分に存在するデータより多くのデータを読み取ろうとすると、ストリームはバイト単位読み取りの場合は -1 を返し、プリミティブデータ読み取り (readInt、readFloat など) の場合は EOFException をスローし、オブジェクト読み取りの場合は eof フィールドが true に設定された OptionalDataException をスローします。
オプションデータのフォーマット、構造体、バージョン管理の責任は、完全にクラスにあります。オプションデータのフォーマットおよび構造体を文書化する場合は、readObject メソッドの javadoc コメント内で @serialData javadoc タグを使うようにしてください。
復元するクラスが読み取るストリーム内に存在しない場合、readObjectNoData メソッドが (定義されている場合) 呼び出され (readObject の代わりに)、そうでない場合はそのフィールドは適切なデフォルト値に初期化されます。詳細は、セクション 3.5 を参照してください。
ObjectInputStream からのオブジェクトの読み込みは、新しいオブジェクトの作成に似ています。新しいオブジェクトのコンストラクタがスーパークラスからサブクラスの順番で呼び出されるのと同様に、ストリームから読み込まれるオブジェクトは、スーパークラスからサブクラスに直列化復元されます。直列化復元中は、Serializable サブクラスごとに、コンストラクタの代わりに readObject または readObjectNoData メソッドが呼び出されます。
コンストラクタと readObject メソッドでもう 1 つ似ている点は、どちらも、完全に構築されていないオブジェクトでメソッドを呼び出す機会を提供することです。オブジェクトの構築中に呼び出されるオーバーライド可能なメソッド (private、static、final のどれでもないもの) は、サブクラスによって潜在的にオーバーライドされている場合があります。オブジェクトの構築段階に呼び出されるメソッドは、コンストラクタまたは readObject/readObjectNoData メソッドによって現在初期化されている型ではなく、オブジェクトの実際の型によって解決されます。したがって、readObject または readObjectNoData メソッド内からオーバーライド可能なメソッドを呼び出すと、スーパークラスが完全に初期化される前にサブクラスが意図せずに呼び出される可能性があります。
readObjectNoData メソッドを使って独自のフィールドの初期化を制御できます。これは、受け取り側が送り側とは異なるバージョンの直列化復元されたインスタンスのクラスを使用し、受け取り側のバージョンが送り側のバージョンによって継承されないクラスを継承する場合に発生する可能性があります。また、直列化ストリームが改変された場合にも発生することがあります。したがって、readObjectNoData は、「悪意のある」または不完全なソースストリームであっても、直列化復元されたオブジェクトを正しく初期化するのに役立ちます。private void readObjectNoData() throws ObjectStreamException;
直列化可能クラスごとに、独自の readObjectNoData メソッドを定義できます。直列化可能クラスが readObjectNoData メソッドを定義しない場合、上記の状況では、クラスのフィールドはデフォルト値 (「Java Language Specification に記載) に初期化されます。この動作は、readObjectNoData メソッドのサポートが導入された Java 2 SDK, Standard Edition Version 1.4 より前の ObjectInputStream の動作と一致します。直列化可能クラスが readObjectNoData メソッドを定義していて、前述の状況が発生した場合には、直列化復元中のその時点で readObjectNoData が呼び出されます (そのクラスがストリームによって、直列化復元するインスタンスのスーパークラスとしてリストされた場合は、クラス定義の readObject メソッドが呼び出される)。
java.io.Externalizable を実装するオブジェクトが、オブジェクトの状態全体を復元するには、readExternal メソッドを実装する必要があります。スーパークラスの状態を復元するには、スーパークラスと連携する必要があります。ObjectInput のすべてのメソッドを、オブジェクトのプリミティブ型フィールドとオブジェクトフィールドを復元するために使用できます。 public void readExternal(ObjectInput stream)
throws IOException;
readExternal メソッドは public であるため、クライアントがストリームの既存オブジェクトを上書きしてしまう危険があります。適切なときにだけ呼び出されるように、クラスに独自のチェックを追加することもできます。
Externalizable オブジェクトの問題を修正するために、JDK 1.2 で新しいストリームプロトコルバージョンが導入されました。Externalizable オブジェクトの以前の定義では、ストリームから Externalizable オブジェクトを適切に読み込めるようにするために、ローカル仮想マシンが readExternal メソッドを探す必要がありました。新しいフォーマットによって十分な情報がストリームプロトコルに追加されるので、ローカル readExternal メソッドが使えない場合に直列化は Externalizable オブジェクトをスキップできます。クラス展開規則により、ローカルクラスを使用するオブジェクトのマッピングがない場合は、直列化は入力ストリーム内の Externalizable オブジェクトをスキップできる必要があります。
新しい Externalizable ストリームフォーマットには、利用できる External データより多くのデータの読み取りを ObjectInputStream が検出し、readExternal メソッドが消費されていないデータをスキップできるという利点もあります。External データの終わりを超えた読み取りに反応する ObjectInputStream の動作は、クラス定義 readObject メソッドがオプションデータの終わりを超えて読み取ろうとしたときの動作と同じです。バイト単位読み取りは -1 を返し、プリミティブ読み取りは EOFException をスローし、オブジェクト読み取りは eof フィールドが true に設定された OptionalDataException をスローします。
フォーマット変更のため、JDK 1.1.6 以前のリリースは新しいフォーマットを読み取れません。JDK 1.1.6 以前が PROTOCOL_VERSION_2 で書き込まれたストリームから Externalizable オブジェクトを読み取ろうとすると、StreamCorruptedException がスローされます。互換性の問題については、セクション 6.3「ストリームプロトコルバージョン」を参照してください。
readResolve メソッドを使うことによって、呼び出し側に返される前に、ストリームから読み込んだオブジェクトを置換または解決できます。readResolve メソッドを実装することによって、クラスは、クラス自体の直列化復元されているインスタンスの型およびインスタンスを直接制御できます。このメソッドは、次のように定義します。 ANY-ACCESS-MODIFIER Object readResolve()
throws ObjectStreamException;
readResolve メソッドは、ObjectInputStream がストリームからオブジェクトを読み込み、呼び出し側に返す準備をしているときに呼び出されます。ObjectInputStream は、オブジェクトのクラスが readResolve メソッドを定義しているかどうかを確認します。メソッドが定義されている場合は、ストリーム内のオブジェクトが、返されるオブジェクトを指定できるように、readResolve メソッドが呼び出されます。返されるオブジェクトは、すべての使用場面で互換性がある型でなければいけません。互換性がない場合は、型の不一致が発見された時点で ClassCastException がスローされます。たとえば、Symbol クラスを作成して、シンボルバインディングごとにインスタンスが仮想マシン内に 1 つだけ存在していたとします。readResolve メソッドは、そのシンボルがすでに定義されているかどうかを判断し、アイデンティティー制約を保持するために既存の等価 Symbol オブジェクトを置換するために実装されます。こうすることで、Symbol オブジェクトの一意性を直列化をまたがって保持できます。
readResolve メソッドはオブジェクトで呼び出されないため、オブジェクトグラフ内でのこのオブジェクトへの参照は、readResolve によって指名された新しいオブジェクトに更新されません。しかし、writeReplace メソッドによるオブジェクトの直列化の間に、置換オブジェクトのオブジェクトグラフ内での元のオブジェクトへのすべての参照は、置換オブジェクトへの参照に置き換えられます。したがって、直列化されているオブジェクトが置換オブジェクト (そのオブジェクトグラフが元のオブジェクトへの参照を持つ) を指名している場合は、直列化復元によって間違ったオブジェクトグラフになります。さらに、読み取っているオブジェクト (writeReplace によって指名された) と元のオブジェクトの参照型が互換でない場合、オブジェクトグラフの構築は ClassCastException をスローします。