目次 | |

Javaオブジェクト直列化仕様: 3 - オブジェクト入力クラス

機械翻訳について

3.1 ObjectInputStreamクラス

クラス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. クラスのインスタンスが割り当てられます。 インスタンスとそのハンドルが既知のオブジェクトのセットに追加されます。 内容が適切に復元されます。

    1. 直列化可能オブジェクトの場合、最初の非直列化可能スーパー・タイプの引数なしコンストラクタが実行されます。 直列化可能クラスの場合、その型に適したデフォルト値にフィールドが初期化されます。 次に、クラス固有のreadObjectメソッドか、これらが定義されていない場合はdefaultReadObjectメソッドが呼び出されて、各クラスのフィールドが復元されます。 直列化復元時には、直列化可能クラスのフィールド・イニシャライザおよびコンストラクタは実行されません。 通常、このストリームを書き込んだクラスのバージョンは、ストリームを読み込むクラスと同じです。 この場合、ストリーム内のオブジェクトのすべてのスーパー・タイプは、現在ロードされているクラスのスーパー・タイプと一致します。 ストリームを書き込んだクラスのバージョンのスーパー・タイプが、ロードされているクラスのスーパー・タイプと異なる場合は、ObjectInputStreamで異なるクラスの状態を復元または初期化する際に一層の注意が必要です。 それらのクラスについて、ストリーム内で利用可能なデータと、復元するオブジェクトのクラスとを照合する必要があります。 ストリームにはあるがオブジェクトにはないクラスのデータは破棄されます。 オブジェクトにはあるがストリームにはないクラスの場合には、クラス・フィールドはデフォルト直列化によってデフォルト値に設定されます。

    2. Externalizableオブジェクトの場合、クラスの引数なしコンストラクタが実行されてから、オブジェクトの内容を復元するためにreadExternalメソッドが呼び出されます。

  12. オブジェクトのクラスまたはObjectInputStream:のサブクラス(あるいはその両方)による潜在的な置換を処理します。

    1. オブジェクトのクラスがenum型ではなく、適切なreadResolveメソッドを定義している場合は、オブジェクトが自身を置換するためにそのメソッドが呼び出されます。

    2. そして、すでにenableResolveObjectによって使用可能にされている場合は、ストリームのサブクラスがオブジェクトを調べて置換するためにresolveObjectメソッドが呼び出されます。 前の手順で元のオブジェクトを置き換えた場合は、置換オブジェクトでresolveObjectメソッドが呼び出されます。 置換が行われると、既知のオブジェクトのテーブルが更新されて、置換オブジェクトがハンドルに関連付けられます。 置換オブジェクトがreadObjectから返されます。

プリミティブ型を読む取るためのすべてのメソッドは、ストリームのブロック・データ・レコードからのバイトのみを消費します。 ストリーム内の次のアイテムがオブジェクトのときにプリミティブ・データの読込みが行われると、読込みメソッドは-1EOFExceptionのうちで適切な方を返します。 プリミティブ型の値は、DataInputStreamによってブロック・データ・レコードから読み込まれます。

スローされる例外は、そのトラバーサル中のエラー、または基になるストリームで起きる例外を反映します。 例外がスローされた場合、基になるストリームは不明で使用不能な状態になります。

ストリームでリセット・トークンが起こると、ストリームのすべての状態は破棄されます。 既知のオブジェクトのセットはクリアされます。

ストリームで例外トークンが起こると、例外が読み込まれ、終了させた例外を引数として新しいWriteAbortedExceptionがスローされます。 ストリーム・コンテキストは前に述べたようにリセットされます。

ストリームから非共有オブジェクトを読み込むには、readUnsharedメソッドを使用します。 このメソッドはreadObjectと同じですが、後続のreadObjectおよびreadUnsharedの呼出しが、元のreadUnsharedの呼出しによって返される直列化復元インスタンスへの追加参照を返すことができない点が異なります。 具体的には、次のようになります。

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メソッドは、特別に処理されるクラスClassObjectStreamClassString、および配列のオブジェクトの場合は呼び出されません。 サブクラスのresolveObjectの実装は、オリジナルの代わりに代入または返される置換オブジェクトを返す場合があります。 返されるオブジェクトは、元のオブジェクトのすべての参照と一貫性を持ちそれらに代入可能な型である必要があり、そうでない場合はClassCastExceptionがスローされます。 すべての代入は型チェックされます。 ストリーム内の、元のオブジェクトへのすべての参照は、置換オブジェクトへの参照によって置き換えられます。

enableResolveObjectメソッドは、直列化復元の際に、あるオブジェクトを監視したり別のオブジェクトに置換したりするために、ObjectOutputStreamの信頼できるサブクラスによって呼び出されます。 オブジェクトの置換は、enableResolveObjecttrue値で呼び出されるまで無効になっています。 それ以降は、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ではないので、サブクラスによって直接オーバーライドできます。

3.2 ObjectInputStream.GetFieldクラス

ObjectInputStream.GetFieldクラスは、直列化可能フィールドの値を取得するためのAPIを提供します。 ストリームのプロトコルはdefaultReadObject.で使用されるものと同じです。readFieldsを使用して直列化可能なフィールドにアクセスしても、ストリームのフォーマットは変更されません。 それらの値にアクセスするための代替APIを提供するのみです(指定された各直列化可能フィールドに対応するtransientでもstaticでもないフィールドを持つことをクラスに要求しません)。 直列化可能フィールドとは、serialPersistentFieldsを使用して宣言されたフィールド(宣言されていない場合は、オブジェクトのtransientでもstaticでもないフィールド)のことです。 ストリームが読み取られるときに利用可能な直列化可能フィールドは、オブジェクトが直列化されたときにストリームに書き込まれたフィールドです。 ストリームを書き込んだクラスが異なるバージョンの場合は、すべてのフィールドが現在のクラスの直列化可能フィールドに対応するわけではありません。 利用可能なフィールドは、GetFieldオブジェクトのObjectStreamClassから取得できます。

getObjectStreamClassメソッドは、ストリーム内のクラスを表すObjectStreamClassオブジェクトを返します。 これには、直列化可能フィールドのリストが含まれています。

defaultedメソッドは、ストリーム内にフィールドが存在しない場合は、trueを返します。 要求されたフィールドが現在のクラスの直列化可能フィールドでない場合は、IllegalArgumentExceptionがスローされます。

getメソッドは、指定された直列化可能フィールドをストリームから返します。 基になるストリームが例外をスローした場合は、入出力例外がスローされます。 名前または型が現在のクラスの直列化可能フィールドの名前および型に一致しない場合は、IllegalArgumentExceptionがスローされます。 フィールドの明示的な値がストリームに含まれていない場合は、デフォルト値が返されます。

3.3 ObjectInputValidationインタフェース

このインタフェースを使用することで、オブジェクトの完全なグラフ(オブジェクトが構成する完全グラフ)が直列化復元されたときに、オブジェクトを呼び出すことができます。 オブジェクトを有効にできない場合は、ObjectInvalidExceptionをスローするはずです。 validateObject呼出し中に例外が発生すると、検証処理が終了し、InvalidObjectExceptionがスローされます。

package java.io;

public interface ObjectInputValidation
{
    public void validateObject()
        throws InvalidObjectException;
}

3.4 readObjectメソッド

直列化可能オブジェクトの場合、クラスはreadObjectメソッドによって独自のフィールドの直列化復元を制御できます。 そのシグニチャを次に示します。

private void readObject(ObjectInputStream stream)
    throws IOException, ClassNotFoundException;

直列化可能オブジェクトの各サブクラスは、独自のreadObjectメソッドを定義できます。 クラスがメソッドを実装しない場合は、defaultReadObjectによって提供されるデフォルト直列化が使用されます。 実装されたクラスは、そのスーパー・タイプまたはサブタイプのフィールドではなく、独自のフィールドの復元だけに責任を持ちます。

クラスのreadObjectメソッドは(実装されている場合)、そのクラスの状態を復元する責任を持ちます。 transientかどうかstaticかどうかには関係なく、オブジェクトの各フィールドの値は、フィールド型のデフォルト値に設定されます。 ObjectInputStreamdefaultReadObjectまたはreadFieldsメソッドは、対応するwriteObjectメソッドによって書き出されたオプション・データを読み取る前に、一度のみ呼び出す必要があります。オプション・データを読み取らない場合でも、defaultReadObjectまたはreadFieldsを一度呼び出す必要があります。 クラスのreadObjectメソッドが、このクラスのストリームのオプション部分に存在するデータより多くのデータを読み取ろうとすると、ストリームはバイト単位読取りの場合は-1を返し、プリミティブ・データ読取り(readIntreadFloatなど)の場合は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メソッド内からオーバーライド可能なメソッドを呼び出すと、スーパー・クラスが完全に初期化される前にサブクラスが意図せずに呼び出される可能性があります。

3.5 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メソッドが呼び出される)。

3.6 readExternalメソッド

java.io.Externalizableを実装するオブジェクトが、オブジェクトの状態全体を復元するには、readExternalメソッドを実装する必要があります。 スーパー・クラスの状態を復元するには、スーパー・クラスと連携する必要があります。 ObjectInputのすべてのメソッドを、オブジェクトのプリミティブ型フィールドとオブジェクト・フィールドを復元するために使用できます。

public void readExternal(ObjectInput stream)
    throws IOException;

ノート: readExternalメソッドはパブリックであり、クライアントがストリームから既存のオブジェクトを上書きする危険性があります。 適切なときにだけ呼び出されるように、クラスに独自のチェックを追加することもできます。

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、"ストリーム・プロトコル・バージョン"」で詳しく説明しています。

3.7 readResolveメソッド

SerializableクラスとExternalizableクラスの場合、クラスは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をスローします。


Copyright © 2005, 2017, Oracle and/or its affiliates. All rights reserved.

目次||