目次|前|次 | 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 (ダイナミック・プロキシ・クラス以外のクラスを表す場合。ストリーム・データで示される)を読み取るために呼び出されます。クラス記述子が動的プロキシ・クラスを表す場合は、記述子のローカル・クラスを取得するためにストリーム上で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
から返される参照が一意であることを保証するとはかぎりません。直列化復元されたオブジェクトで定義されているreadResolve
メソッドが、他の関係者が見ることができるオブジェクトを返したり、readUnshared
がストリームの内部または外部から取得できるClass
オブジェクトまたはenum定数を返したりする可能性があります。直列化復元されたオブジェクトが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セキュリティ・アーキテクチャ(JDK 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
をスローします。