目次 | 前の項目 | 次の項目 | 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 コメント内の@serialData
javadoc タグを使う必要があります。復元するクラスが読み込みストリーム内に存在しない場合、
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
が発生します。