目次 | 前へ | 次へ | 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
をスローします。