目次 | 前の項目 | 次の項目 Java オブジェクト直列化仕様
Version 6.0

オブジェクト入力クラス






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

enableResolveObject メソッドは、直列化復元の際に、あるオブジェクトを別のオブジェクトの代わりにモニターしたり、あるオブジェクトで別のオブジェクトを置換することを可能にするために、ObjectOutputStream の信頼できるサブクラスによって呼び出されます。オブジェクトの置換は、enableResolveObjecttrue 値で呼び出されるまでは、使用不可になっています。また、使用可能にしたあとで、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 メソッドは、ファイナルではないので、サブクラスによって直接オーバーライドされます。


3.2 ObjectInputStream.GetField クラス

ObjectInputStream.GetField は、直列化可能フィールドの値を取得するための API を提供します。ストリームのプロトコルは、defaultReadObject が使うプロトコルと同じです。readFields の使用による直列化可能フィールドへのアクセスでは、ストリームの形式は変更されずに、値にアクセスするための代わりの API が提供されるだけです。この 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 メソッド

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

    private void readObject(ObjectInputStream stream)
        throws IOException, ClassNotFoundException;
Serializable オブジェクトの各サブクラスは、それ独自の 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 メソッドを定義しない場合、上記の状況では、クラスのフィールドはデフォルト値 (「JavaTM Language Specification, Second Edition」の 4.5.5 に記載) に初期化されます。この動作は、readObjectNoData メソッドのサポートが導入された JavaTM 2 SDK, Standard Edition Version 1.4 以前の ObjectInputStream の動作と一致します。直列化可能クラスが readObjectNoData メソッドを定義する場合に前述の状況が発生すると、クラス定義の readObject メソッドが呼び出されたときに、そのクラスがストリームによって直列化復元するインスタンスのスーパークラスとしてリストされていれば、直列化復元中のその時点で readObjectNoData が呼び出されます。


3.6 readExternal メソッド

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 ストリームプロトコルのバージョン」を参照してください。


3.7 readResolve メソッド

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 が発生します。


目次 | 前の項目 | 次の項目
Copyright © 2005 Sun Microsystems, Inc. All Rights Reserved.