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

オブジェクト出力クラス






第 2 章

トピック:


2.1 ObjectOutputStream クラス

クラス ObjectOutputStream は、オブジェクト直列化を実装するためのものです。このクラスは、すでに直列化されたオブジェクトセットなどのストリームの状態を維持します。そのメソッドは、直列化するオブジェクトのトラバーサルを制御して、指定されたオブジェクトと参照するオブジェクトを保管します。

package java.io;

public class ObjectOutputStream
    extends OutputStream
    implements ObjectOutput, ObjectStreamConstants
{
    public ObjectOutputStream(OutputStream out)
        throws IOException;

    public final void writeObject(Object obj)
        throws IOException;

    public void writeUnshared(Object obj)
        throws IOException;

    public void defaultWriteObject()
        throws IOException, NotActiveException;

    public PutField putFields()
        throws IOException;

    public writeFields()
        throws IOException;

    public void reset() throws IOException;

    protected void annotateClass(Class cl) throws IOException;

    protected void writeClassDescriptor(ObjectStreamClass desc)
        throws IOException;

    protected Object replaceObject(Object obj) throws IOException;

    protected boolean enableReplaceObject(boolean enable)
        throws SecurityException;

    protected void writeStreamHeader() throws IOException;

    public void write(int data) throws IOException;

    public void write(byte b[]) throws IOException;

    public void write(byte b[], int off, int len) throws IOException;

    public void flush() throws IOException;

    protected void drain() throws IOException;

    public void close() throws IOException;

    public void writeBoolean(boolean data) throws IOException;

    public void writeByte(int data) throws IOException;

    public void writeShort(int data) throws IOException;

    public void writeChar(int data) throws IOException;

    public void writeInt(int data) throws IOException;

    public void writeLong(long data) throws IOException;

    public void writeFloat(float data) throws IOException;

    public void writeDouble(double data) throws IOException;

    public void writeBytes(String data) throws IOException;

    public void writeChars(String data) throws IOException;

    public void writeUTF(String data) throws IOException;

    // Inner class to provide access to serializable fields.
    abstract static public class PutField
    {
        public void put(String name, boolean value)
            throws IOException, IllegalArgumentException;

        public void put(String name, char data)
            throws IOException, IllegalArgumentException;

        public void put(String name, byte data)
            throws IOException, IllegalArgumentException;

        public void put(String name, short data)
            throws IOException, IllegalArgumentException;

        public void put(String name, int data)
            throws IOException, IllegalArgumentException;

        public void put(String name, long data)
            throws IOException, IllegalArgumentException;

        public void put(String name, float data)
            throws IOException, IllegalArgumentException;

        public void put(String name, double data)
            throws IOException, IllegalArgumentException;

        public void put(String name, Object data)
            throws IOException, IllegalArgumentException;
    }

    public void useProtocolVersion(int version) throws IOException;

    protected ObjectOutputStream()
        throws IOException;

     protected writeObjectOverride()
        throws NotActiveException, IOException;
}
単一引数の ObjectOutputStream コンストラクタは、指定された OutputStream にオブジェクトを直列化する ObjectOutputStream を作成します。このコンストラクタは、writeStreamHeader を呼び出して、マジック番号とバージョンをストリームに書き込みます。そして、このストリームは、単一引数の ObjectInputStream コンストラクタの対応する readStreamHeader を呼び出すことによって読み込まれ、検査されます。セキュリティーマネージャーがインストールされている場合、このコンストラクタは、putFields メソッドまたは writeUnshared メソッド、あるいはその両方をオーバーライドするサブクラスのコンストラクタによって直接または間接的に呼び出されたときに、"enableSubclassImplementation" SerializablePermission のチェックを行います。

writeObject メソッドは、オブジェクトをストリームに直列化するために使用します。オブジェクトは、次のように直列化されます。

  1. サブクラスが実装をオーバーライドする場合は、writeObjectOverride メソッドを呼び出してから、復帰します。実装のオーバーライドは、この節の最後で説明します。
  2. ブロックデータバッファーにデータがあれば、それがストリームに書き込まれ、バッファーがリセットされます。
  3. オブジェクトが null であれば、null がストリームに置かれ、writeObject から戻ります。
  4. 手順 8 に示したように、オブジェクトが以前に置き換えられていれば、置き換えのハンドルをストリームに書き込み、writeObject から戻ります。
  5. オブジェクトがストリームにすでに書き込まれていれば、そのハンドルがストリームに書き込まれ、writeObject から戻ります。
  6. オブジェクトが Class であれば、対応する ObjectStreamClass がストリームに書き込まれ、そのクラスのハンドルが割り当てられ、writeObject から戻ります。
  7. オブジェクトが ObjectStreamClass の場合、そのオブジェクトは 4.3 で説明するクラス記述子の書式の 1 つを使用してストリームに書き込まれ、ハンドルが割り当てられます。Version 1.3 以降の Java TM 2 SDK, Standard Edition では、ObjectStreamClass がダイナミックプロキシクラス以外のクラスを表す場合、writeClassDescriptor メソッドが呼び出され、そのクラスを出力します。 ダイナミックプロキシクラスであるかどうかは、関連付けされた Class オブジェクトを java.lang.reflect.ProxyisProxyClass メソッドに渡すことで判別されます。その後、クラスがダイナミックプロキシクラスであれば annotateProxyClass メソッドが呼び出され、そうでない場合は annotateClass メソッドが呼び出されて、表されているクラスの注釈が書き込まれます。すると、writeObject メソッドが返されます。
  8. オブジェクトのクラスまたは ObjectInputStream のサブクラスによる潜在的な置換を処理します。
  a. オブジェクトのクラスが enum 型でなく、また適切な writeReplace メソッドを定義する場合は、そのメソッドが呼び出されます。選択により、代替オブジェクトを戻して直列化することもできます。
  b. enableReplaceObject メソッドの呼び出しにより replaceObject メソッドが有効に設定されている場合、このメソッドを呼び出すことにより、直列化中のオブジェクトの代わりに ObjectOutputStream のサブクラスを使用できます。前の手順で元のオブジェクトを置き換えた場合は、置換オブジェクトで replaceObject メソッドが呼び出されます。
  上記の一方または両方の手順で元のオブジェクトを置き換えた場合、元のオブジェクトから置換オブジェクトへのマッピングが (手順 4 で利用するために) 記録されます。その後、新規オブジェクトに対し、手順 3 ~ 7 が繰り返されます。
  置換オブジェクトが、手順 3 ~ 7 を適用できないタイプの場合、手順 10 で置換オブジェクトを使った処理が再開されます。
  9. オブジェクトが java.lang.String, の場合、文字列は長さの情報として書き込まれ、その情報のあとに変更後の UTF-8 で符号化された文字列の内容が続きます。 詳細は、「6.2 ストリーム要素」を参照してください。ハンドルが文字列に割り当てられ、writeObject が返されます。
  10. オブジェクトが配列の場合、writeObject が再帰的に呼び出されて、配列の ObjectStreamClass が書き込まれます。配列用のハンドルが割り当てられます。その直後に、配列の長さの分だけ続きます。その後、配列の各要素がストリームに書き込まれて、writeObject が返されます。
  11. オブジェクトが enum 定数であれば、writeObject を再帰的に呼び出して、その定数の enum 型に対する ObjectStreamClass が書き込まれます。このストリームへの書き込みは、オブジェクトの最初の参照時だけです。この enum 定数のハンドルが割り当てられます。次に、手順 9 で説明したように、enum 定数の name メソッドで返された値が String オブジェクトとして書き込まれます。 以前ストリームに同じ名前の文字列があった場合には、その名前に対するバック参照が書き込まれます。すると、writeObject メソッドが返されます。
  12. 正規オブジェクトの場合、writeObject を再帰的に呼び出すことにより、オブジェクトのクラスの ObjectStreamClass が書き込まれます。このストリームへの書き込みは、オブジェクトの最初の参照時だけです。このオブジェクト用のハンドルが割り当てられます。
  13. オブジェクトの内容が、ストリームに書き込まれます。
  a. 直列化可能オブジェクトの場合、最上位の直列化可能クラスが配置されます。そのクラス、およびそのクラスから派生したクラスごとに、クラスのフィールドが書き込まれます。クラスが writeObject メソッドを保持しない場合、defaultWriteObject メソッドが呼び出されて、直列化可能フィールドのストリームへの書き込みが行われます。クラスが writeObject メソッドを保持する場合、このメソッドが呼び出されます。defaultWriteObjectputFields のどちらか、および writeFields を呼び出してオブジェクトの状態を保存し、その後、他の情報をストリームに書き込むことができます。
  b. オブジェクトが外部化可能な場合、オブジェクトの writeExternal メソッドが呼び出されます。
  c. オブジェクトが直列化可能でも、外部化可能でもない場合、NotSerializableException がスローされます。
例外は、トラバーサル中に発生する場合も、基礎ストリーム内で発生する場合もあります。IOException のすべてのサブクラスで、例外プロトコルを使って例外がストリームに書き込まれ、ストリーム状態が破棄されます。最初の例外をストリームに書き込んでいる間に 2 番目の IOException がスローされると、ストリームの状態は不明のままで、writeObject から StreamCorruptedException がスローされます。その他の例外の場合、ストリームは中止され、不明で使用不能な状態のままになります。

writeUnshared メソッドは、「非共有」オブジェクトを ObjectOutputStream に書き込みます。このメソッドは、指定されたオブジェクトを常に新しい一意のオブジェクトとしてストリームに書き込む (以前に直列化されたインスタンスへのバック参照とは対照的) 点を除いては、writeObject と同じです。具体的には、次のようになります。

writeUnshared を介してオブジェクトを書き込むこと自体は、オブジェクトが直列化復元されたときにそのオブジェクトへの一意参照を保証するものではありませんが、1 つのオブジェクトがストリーム内で複数回定義されることは可能になります。そのため、受け取り側が ObjectInputStream.readUnshared メソッド (「3.1 ObjectInputStream クラス」を参照) を複数回呼び出しても衝突が生じません。ここで説明した規則は、writeUnshared によって書き込まれた基本レベルのオブジェクトだけに適用され、直列化されるオブジェクトのグラフ内で一時的に参照されるサブオブジェクトには一切適用されません。

defaultWriteObject メソッドは、現在のクラスに対するデフォルトの直列化機構を実装します。このメソッドの呼び出しは、クラスの writeObject メソッドからのみ可能です。このメソッドは、現在のクラスの直列化可能フィールドすべてをストリームに書き込みます。writeObject メソッドの外部からこのメソッドが呼び出されると、NotActiveException がスローされます。

putFields メソッドは、ストリーム内の直列化可能フィールドの値を設定する際に呼び出し側が使用する PutField オブジェクトを返します。フィールドは、任意の順序で設定できます。すべてのフィールドの設定が完了したら、writeFields を呼び出してフィールド値を規定の順序でストリームに書き込む必要があります。フィールドが設定されない場合、そのフィールドタイプに適したデフォルト値がストリームに書き込まれます。このメソッドは、直列化可能クラスの writeObject メソッド内からしか呼び出すことができません。また、このメソッドは、1 回しか呼び出すことができず、defaultWriteObject がすでに呼び出されている場合は呼び出せません。writeFields を呼び出したあとでないと、他のデータをストリームに書き込むことはできません。

reset メソッドは、ストリーム状態を再設定して、構成時の状態に戻します。Reset により、それまでストリームに書き込まれたすべてのオブジェクト状態は破棄されます。ストリーム内の現在位置に再設定のマークが付けられるため、対応する ObjectInputStream も同じ位置で再設定されます。以前にストリームに書き込まれたオブジェクトが、ストリームに書き込み済みのオブジェクトとして記憶されることはありません。これらのオブジェクトは、ストリームに再度書き込まれます。これは、オブジェクトの内容やオブジェクトを再送信しなければならない場合に有用です。オブジェクトの直列化時に Reset が呼び出されない場合もあります。不正な仕方で呼び出されると、IOException がスローされます。

JavaTM 2 SDK, Standard Edition, v1.3 から、ObjectStreamClass の直列化が必要になると、writeClassDescriptor が呼び出されるようになりました。writeClassDescriptor は、ObjectStreamClass 表現の直列化ストリームへの書き込みを担当します。サブクラスでこのメソッドをオーバーライドすることにより、クラス記述子の直列化ストリームへの書き込み方法をカスタマイズできます。このメソッドをオーバーライドする場合は、ObjectInputStream 内の対応する readClassDescriptor メソッドもオーバーライドして、カスタムストリーム表現からクラス記述子を再構成する必要があります。デフォルトでは、writeClassDescriptor は、「6.4 ストリーム形式の文法」で指定された形式に従ってクラス記述子を書き込みます。このメソッドは、ObjectOutputStream が以前の直列化ストリーム形式を使用していない場合にのみ、呼び出し可能である点に留意してください (「6.3 ストリームプロトコルのバージョン」を参照)。直列化ストリームに以前の形式 (ObjectStreamConstants.PROTOCOL_VERSION_1) が使用されている場合、クラス記述子はオーバーライドまたはカスタマイズ不可能な方法で内部に書き込まれます。

Class の直列化中、かつクラス記述子のストリームへの書き込み後に、annotateClass メソッドが呼び出されます。サブクラスがこのメソッドを継承して、クラスに関する他の情報をストリームに書き込むことも可能です。この情報の読み取りは、対応する ObjectInputStream サブクラスの resolveClass メソッドを使って実行する必要があります。

ObjectOutputStream サブクラスは、replaceObject メソッドを実装することにより、直列化の過程でのオブジェクトの監視および置換を実行できます。オブジェクトを置換する場合、最初の置換対象オブジェクトに対し writeObject を呼び出す前に、enableReplaceObject を呼び出して置換を明示的に有効にする必要があります。オブジェクトの置換が有効にされた後、オブジェクトを初めて直列化する直前に、各オブジェクトに対して replaceObject が呼び出されます。replaceObject メソッドは、特別に処理されるクラスである ClassObjectStreamClass のオブジェクトに対しては呼び出されません。サブクラスの実装が、元のオブジェクトではなく直列化される代替オブジェクトを返す場合があります。代替オブジェクトは、直列化可能でなければなりません。ストリームにおける元のオブジェクトへのすべての参照は、置換オブジェクトによって置き換えられます。

オブジェクトの置換中に、サブクラスは、代替オブジェクトと参照の格納される全フィールドとの互換性が保たれていること、または直列化復元時に相補置換が行われることを保証する必要があります。オブジェクトのタイプがフィールドまたは配列要素のタイプのサブクラスではない場合、そのオブジェクトは、ClassCastException をスローすることにより直列化復元を中止します。参照は格納されません。

enableReplaceObject メソッドは、直列化の際に、あるオブジェクトで別のオブジェクトを代用することを可能にするために、信頼できる ObjectOutputStream のサブクラスが呼び出します。オブジェクトの置換は、enableReplaceObjecttrue 値で呼び出されるまでは、使用不可になっています。また、使用可能にしたあとで、false に設定して、使用不可にされる場合があります。前の設定が返されます。enableReplaceObject は、置換を要求するストリームを信頼できるかどうかを調べます。オブジェクトの private 状態が意図せずに公開されないことを保証するために、信頼できるストリームサブクラスだけが replaceObject を使用することを許可されます。信頼されるクラスは、Serializable 置換を有効にする権限を保持する、セキュリティー保護ドメインに属するクラスです。

ObjectOutputStream のサブクラスがシステムドメインの一部とはみなされない場合、SerializablePermission "enableSubstitution" をセキュリティーポリシーファイルに追加する必要があります。ObjectInputStream のサブクラスの保護ドメインに、enableReplaceObject の呼び出しによる "enableSubstitution" の権限がない場合は、AccessControlException がスローされます。セキュリティーモデルの詳細は、JavaTM セキュリティーアーキテクチャー (JDK1.2) のドキュメントを参照してください。

writeStreamHeader メソッドは、マジック番号とバージョンをストリームに書き込みます。この情報は、ObjectInputStreamreadStreamHeader メソッドを使って読み取る必要があります。ストリームの一意な形式を識別するために、サブクラスがこのメソッドを実装することが必要な場合があります。

flush メソッドを使用して、ストリームが保持するバッファーを空にして、基礎ストリームにフラッシュを転送します。基礎ストリームのフラッシュを強制せずに ObjectOutputStream のバッファーだけを空にする場合、サブクラスから drain メソッドを使用できます。

プリミティブ型の書き込みメソッドはすべて、DataOutputStream を使って値を符号化して、標準ストリーム形式にします。バイトがブロックデータレコードにバッファリングされることにより、オブジェクトのエンコーディングとの区別が可能になります。このバッファリングにより、クラスのバージョン管理が必要な場合、プリミティブデータのスキップも可能になります。また、クラス固有のメソッドを呼び出すことなく、ストリームの構文解析を行うことも可能になります。

直列化の実装をオーバーライドするために、ObjectOutputStream のサブクラスは、保護された引数なしの ObjectOutputStream コンストラクタを呼び出す必要があります。SerializablePermission "enableSubclassImplementation" の引数なしのコンストラクタ内にはセキュリティーチェックがあり、信頼できるクラスだけにデフォルトの実装のオーバーライドを許可します。このコンストラクタは、ObjectOutputStream に private なデータを割り当てず、ファイナルの writeObject メソッドは writeObjectOverride メソッドを呼び出してから復帰することを示すフラグを設定します。ほかのすべての ObjectOutputStream メソッドは、ファイナルではないので、サブクラスによって直接オーバーライドされます。


2.2 ObjectOutputStream.PutField クラス

PutField クラスは、あるクラスがデフォルトの直列化を使わない場合に、そのクラスの直列化可能フィールドの値を設定するための API を提供します。各メソッドは、指定された名前付きの値をストリームに配置します。name が書き込み先であるクラスの直列化可能フィールドの名前と一致しない場合、または命名されたフィールドのタイプが呼び出される put メソッドの 2 番目のパラメータタイプと一致しない場合、IllegalArgumentException がスローされます。


2.3 writeObject メソッド

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

    private void writeObject(ObjectOutputStream stream)
        throws IOException;
Serializable オブジェクトの各サブクラスは、自らの writeObject メソッドを定義することができます。クラスにこのメソッドが実装されていなければ、defaultWriteObject によって与えられるデフォルトの直列化が使用されます。実装されている場合は、そのクラスは、そのスーパータイプやサブタイプのフィールドではなく、それ独自のフィールドだけを書き込む責任があります。

クラスの writeObject メソッドは、実装されている場合、そのクラスの状態を保管する責任があります。ObjectOutputStreamdefaultWriteObject メソッドまたは writeFields メソッドを一度 (一度だけ) 呼び出してからでないと、対応する readObject メソッドでそのオブジェクトの状態を復元するために必要になる任意指定のデータを書き込むことはできません。任意指定のデータを書き込まない場合でも、defaultWriteObject または writeFields を一度呼び出す必要があります。任意指定データ (ある場合) の書き込みの前に defaultWriteObjectwriteFields が呼び出されなければ、ObjectInputStream がその writeObject メソッドを定義したクラスを解決できない場合に、インスタンスの直列化復元の動作は未定義になります。

この任意指定データの形式、構造体、バージョン管理の責任のすべては、そのクラスにあります。


2.4 writeExternal メソッド

java.io.Externalizable を実装するオブジェクトは、writeExternal メソッドを実装して、そのオブジェクトの状態全体を保存する必要があります。このオブジェクトは、そのスーパークラスと協調して、それらの状態を保管しなければなりません。ObjectOutput のすべてのメソッドが、オブジェクトのプリミティブ型フィールドとオブジェクトフィールドを保管するために使用できます。

    public void writeExternal(ObjectOutput stream)
        throws IOException;
JDKTM 1.2 で、外部化可能データを書き込むためのデフォルト形式が導入されました。新しい形式では、プリミティブデータは、writeExternal メソッドによってブロックデータモードで書き込まれるように指定されています。さらに、writeExternal メソッドから戻ったあとに、ストリームの末尾に外部オブジェクトの末尾を示すタグが追加されます。この形式変更の利点については、「3.6 readExternal メソッド」を参照してください。この変更により生じる互換性の問題の詳細は、「2.6 useProtocolVersion メソッド」を参照してください。


2.5 writeReplace メソッド

Serializable および Externalizable クラスの場合には、writeReplace メソッドは、オブジェクトが書き込まれる前に、オブジェクトのクラスがストリーム内で自らの置換を指定することを許可します。writeReplace メソッドを実装することにより、クラスは、直列化されている自らのインスタンスの型およびインスタンスを直接制御できます。

メソッドは、次のように定義されます。

	ANY-ACCESS-MODIFIER Object writeReplace() {
   		 throws ObjectStreamException;
ObjectOutputStream がストリームにオブジェクトを書き込む準備をしているとき、writeReplace メソッドが呼び出されます。ObjectOutputStream は、クラスが writeReplace メソッドを定義しているかどうかをチェックします。このメソッドが定義される場合は、writeReplace メソッドが呼び出されて、オブジェクトがストリーム内でその置換を指定できるようにします。返されるオブジェクトは、渡されるオブジェクトと同じ型であるか、あるいは読み込みおよび解釈処理後の型が、そのオブジェクトへのすべての参照と互換性がなければなりません。そうでない場合、型の不一致が検出されたときに、ClassCastException が発生します。


2.6 useProtocolVersion メソッド

下位互換性のないストリームプロトコルへの変更のため、現在の仮想マシンでは、以前のリリースが読み取り可能な直列化ストリームを書き込むことができる機構が追加されました。ただし、下位互換性のあるプロトコルを使う場合は、新しいストリーム形式では修正されている問題が発生する可能性があります。

ストリームプロトコルのバージョンについては、「6.3 ストリームプロトコルのバージョン」で説明します。



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