目次 | 前の項目 | 次の項目 Java オブジェクト直列化仕様
バージョン 5.0


オブジェクト直列化ストリームプロトコル

トピック:

  • 概要
  • ストリーム要素
  • ストリームプロトコルのバージョン
  • ストリーム形式の文法
  • 6.1 概要

    ストリーム形式は、次の設計目標を実現しました。

  • 効率的な読み取りを実現するためのコンパクト化および構造化
  • ストリームの構造と形式の知識だけを使ってストリーム内をスキップ可能。クラスごとにコードを呼び出す必要がない
  • データへのストリームアクセスだけが必要

    6.2 ストリーム要素

    基本構造では、ストリーム内でオブジェクトを表す必要があります。オブジェクトのクラス、フィールド、およびクラス固有のメソッドによって書き込まれ、後で読み取られるデータなどの属性を表現する必要があります。ストリームのオブジェクトの表現は、ある文法によって記述することができます。null オブジェクト、新規オブジェクト、クラス、配列、文字列、およびすでにストリームにあるオブジェクトへのバック参照に対し、特別な表現があります。ストリームに書き込まれた各オブジェクトには、このオブジェクトをバック参照するために使用するハンドルが割り当てられます。ハンドルは、0x7E0000 から始めて順次割り当てられます。ストリームがリセットされると、ハンドルは再び 0x7E0000 から始まります。

    クラスオブジェクトは、次の要素によって表されます。

  • その ObjectStreamClass オブジェクト

    ダイナミックプロキシクラス以外のクラスの ObjectStreamClass オブジェクトは、次の要素によって表されます。

  • 互換クラスのストリーム固有識別子 (SUID)
  • クラスのさまざまなプロパティを表すフラグのセット。クラスが writeObject メソッドを定義しているか、クラスが直列化可能、外部化可能、または enum 型か、など
  • 直列化可能フィールドの数
  • デフォルトの機構によって直列化されたクラスのフィールドの配列。
    配列やオブジェクトのフィールドでは、フィールドの型が文字列として格納される。この文字列は、「JavaTM Virtual Machine Specification, Second Edition」の 4.3.2 に指定されているフィールド記述子の形式 (例: Ljava/lang/Object;) でなければならない
  • annotateClass メソッドによって書き込まれる任意指定のブロックデータレコードまたはオブジェクト
  • ObjectStreamClass のスーパータイプ (スーパークラスが直列化可能でなければ null)

    ダイナミックプロキシクラスの ObjectStreamClass オブジェクトは、次の要素によって表されます。

  • ダイナミックプロキシクラスが実装するインタフェース数
  • ダイナミックプロキシクラスが実装するすべてのインタフェースの名前。その Class オブジェクト上で getInterfaces メソッドを呼び出したときに返される順番に表示する
  • annotateProxyClass メソッドによって書き込まれる任意指定のブロックデータレコードまたはオブジェクト
  • ObjectStreamClass のスーパータイプ、java.lang.reflect.Proxy

    String オブジェクトの表現は長さの情報で構成され、その情報のあとに変更後の UTF-8 で符号化された文字列の内容が続きます。変更後の UTF-8 エンコーディングは、JavaTM 仮想マシンと、java.io.DataInput および DataOutput インタフェースで使用されるものと同じです。ただし、補助文字と null 文字の表現で、標準 UTB-8 とは異なります。長さの情報の形式は、変更後の UTF-8 エンコーディングでの文字列の長さにより異なります。指定された String の変更後の UTF-8 エンコーディング長が 65,536 バイト未満の場合、長さは、符号なし 16 ビット整数を表す 2 バイトとして書き込まれます。JavaTM 2 platform, Standard Edition, v1.3 以降は、変更後の UTF-8 エンコーディングによる文字列の長さが 65,536 バイト以上の場合、長さは、符号付き 64 ビット整数を表す 8 バイトで書き込まれます。直列化ストリームの String の前にあるタイプコードは、String の書き込みに使用された形式を表しています。

    配列は次の要素によって表されます。

  • それらの ObjectStreamClass オブジェクト
  • 要素の数
  • 値の順序。値の型はその配列の型に内在している。たとえば、バイト配列の値の型はバイトである

    enum 定数は次の要素によって表されます。

  • 定数の基になっている enum 型の ObjectStreamClass オブジェクト
  • 定数の名前文字列

    ストリームの新規オブジェクトは次の要素によって表されます。

  • そのオブジェクトのもっとも多く派生されたクラス
  • オブジェクトの各直列化可能クラスのデータ (最上位のスーパークラスを先頭とする)。
    各クラスのストリームには以下が含まれる
  • 直列化可能フィールド
    「1.5 クラスの直列化可能なフィールドの指定」を参照
  • そのクラスに writeObject/readObject メソッドがあれば、writeObject メソッドによって書き込まれた、プリミティブ型の任意指定のオブジェクトおよび (または) ブロックデータレコードと、それに続いて endBlockData がある場合がある

    クラスによって書き込まれたすべてのプリミティブデータは、ブロックデータレコードにバッファリングされ、ラップされます。これは、そのデータが writeObject メソッドの中でストリームに書き込まれたのか、writeObject メソッドの外から直接ストリームに書き込まれたのかには関係ありません。このデータは、対応する readObject メソッドによって読み込むか、ストリームから直接読み込むことができます。writeObject メソッドによって書き込まれたオブジェクトは、前に書き込まれたブロックデータレコードがあればそれを停止し、正規オブジェクト、null、またはバック参照のいずれかで、適切なものとして書き込まれます。ブロックデータレコードでは、エラーの回復によって任意指定データを破棄することができます。クラスの中から呼び出された場合には、ストリームはデータやオブジェクトを endBlockData まで破棄することができます。

    6.3 ストリームプロトコルのバージョン

    JDKTM 1.2 では、直列化ストリーム形式を、JDKTM 1.1 のすべてのマイナーリリースと下位互換性のない形式に変更する必要があります。下位互換性が必要な場合に備え、直列化ストリームの書き込み時に、どの PROTOCOL_VERSION を使用するかを示す機能が追加されています。メソッド ObjectOutputStream.useProtocolVersion は、直列化ストリームの書き込みに使うプロトコルのバージョンをパラメータに取ります。

    ストリームプロトコルのバージョンを次に示します。

  • ObjectStreamConstants.PROTOCOL_VERSION_1
    初期のストリーム形式を示します。
  • ObjectStreamConstants.PROTOCOL_VERSION_2
    新しい外部データ形式を示します。プリミティブデータはブロックデータモードで書き込まれ、TC_ENDBLOCKDATA で終了します。
    ブロックデータ境界は標準化されました。ブロックデータモードで書き込まれたプリミティブデータは、1024 バイトのデータのチャンクを超えないように標準のデータ形式に戻されます。この変更の利点は、ストリーム内の直列化データ形式の仕様を強化することです。この変更は、完全に下位および上位互換性があります。

    JDKTM 1.2 は、デフォルトでは PROTOCOL_VERSION_2 を書き込みます。

    JDKTM 1.1 は、デフォルトでは PROTOCOL_VERSION_1 を書き込みます。

    JDKTM 1.1.7 以降では、両方のバージョンを読み取ることができます。

    JDKTM 1.1.7 より前のリリースでは、PROTOCOL_VERSION_1 しか読み取ることができません。

    6.4 ストリーム形式の文法

    以下の表は、ストリーム形式の文法を示したものです。非終端記号はイタリックで、終端記号は、固定幅のフォントで示します。非終端の定義には、その後に「:」が続きます。定義には 1 つまたは複数の代替定義が続き、それぞれが別の行に示されます。次の表に、その表記法を示します。

    表記法 意味

    (datatype)

    このトークンには、バイトなどのデータ型が指定される

    token[n]

    このトークンの事前に定義されたオカレンス数。 これは配列である

    x0001

    16 進数で表したリテラル値。16 進数の桁数がその値のサイズを表す

    <xxx>

    ストリームから読み込まれた値であり、配列の長さを示すために使用される


    記号 (utf) は 2 バイトの長さ情報を使用して記述された文字列を指定する場合に使います。(long-utf) は 8 バイトの長さ情報を使用して記述された文字列を指定する場合に使います。詳細については、「6.2 ストリーム要素」を参照してください。

    6.4.1 文法規則

    直列化されたストリームは、ストリーム規則を満たす任意のストリームによって表されます。

     stream:
      magic version contents
     contents:
      content
      contents content
     content:
      object
      blockdata
     object:
      newObject
      newClass
      newArray
      newString
    newEnum
      newClassDesc
      prevObject
      nullReference
      exception
      TC_RESET
     newClass:
    TC_CLASS classDesc newHandle
     classDesc:
    newClassDesc
    nullReference
    (ClassDesc)prevObject      // an object required to be of type
    // ClassDesc
     superClassDesc:
    classDesc
     newClassDesc:
    TC_CLASSDESC className serialVersionUID newHandle classDescInfo
    TC_PROXYCLASSDESC newHandle proxyClassDescInfo
     classDescInfo:
    classDescFlags fields classAnnotation superClassDesc
     className:
    (utf)
     serialVersionUID:
    (long)
     classDescFlags:
    (byte)                  // Defined in Terminal Symbols and
    // Constants
     proxyClassDescInfo:
    (int)<count> proxyInterfaceName[count] classAnnotation
    superClassDesc
     proxyInterfaceName:
       (utf)
     fields:
    (short)<count>  fieldDesc[count]
     fieldDesc:
    primitiveDesc
    objectDesc
     primitiveDesc:
    prim_typecode fieldName
     objectDesc:
    obj_typecode fieldName className1
     fieldName:
    (utf)
     className1:
    (String)object             // String containing the field's type,
                                 // in field descriptor format
     classAnnotation:
    endBlockData
    contents endBlockData      // contents written by annotateClass
     prim_typecode:
    `B'	 // byte
    `C'	 // char
    `D'	 // double
    `F'	 // float
    `I'	 // integer
    `J'	 // long
    `S'	 // short
    `Z'	 // boolean
     obj_typecode:
    `[`	 // array
    `L'	 // object
     newArray:
    TC_ARRAY classDesc newHandle (int)<size> values[size]
     newObject:
    TC_OBJECT classDesc newHandle classdata[]  // data for each class
     classdata:
    nowrclass                 // SC_SERIALIZABLE & classDescFlag &&
    // !(SC_WRITE_METHOD & classDescFlags)
    wrclass objectAnnotation  // SC_SERIALIZABLE & classDescFlag &&
    // SC_WRITE_METHOD & classDescFlags
    externalContents          // SC_EXTERNALIZABLE & classDescFlag &&
    // !(SC_BLOCKDATA  & classDescFlags
    objectAnnotation          // SC_EXTERNALIZABLE & classDescFlag&& 
    // SC_BLOCKDATA & classDescFlags
     nowrclass:
    values                    // fields in order of class descriptor
     wrclass:
    nowrclass
     objectAnnotation:
    endBlockData
    contents endBlockData     // contents written by writeObject
    // or writeExternal PROTOCOL_VERSION_2.
     blockdata:
    blockdatashort
    blockdatalong
     blockdatashort:
    TC_BLOCKDATA (unsigned byte)<size> (byte)[size]
     blockdatalong:
    TC_BLOCKDATALONG (int)<size> (byte)[size]
     endBlockData	 :
    TC_ENDBLOCKDATA
     externalContent:// Only parseable by readExternal
    ( bytes)                // primitive data
    object
     externalContents:// externalContent written by
    externalContent         // writeExternal in PROTOCOL_VERSION_1.
    externalContents externalContent
     newString:
    TC_STRING newHandle (utf)
    TC_LONGSTRING newHandle (long-utf)
     newEnum:
    TC_ENUM classDesc newHandle enumConstantName
     enumConstantName:
    (String)object
     prevObject
    TC_REFERENCE (int)handle
     nullReference
    TC_NULL
     exception:
    TC_EXCEPTION reset (Throwable)object	  reset
     magic:
    STREAM_MAGIC
     version
    STREAM_VERSION
     values:// The size and types are described by the
    // classDesc for the current object
     newHandle:// The next number in sequence is assigned
    // to the object being serialized or deserialized
     reset:// The set of known objects is discarded
    // so the objects of the exception do not
    // overlap with the previously sent objects
    // or with objects that may be sent after
    // the exception
    

    6.4.2 終端記号と定数

    java.io.ObjectStreamConstants の次の記号は、ストリームで予期される終端値と定数値を定義したものです。

         
    final static short STREAM_MAGIC = (short)0xaced;    
    final static short STREAM_VERSION = 5;
    final static byte TC_NULL = (byte)0x70;
    final static byte TC_REFERENCE = (byte)0x71;
    final static byte TC_CLASSDESC = (byte)0x72;
    final static byte TC_OBJECT = (byte)0x73;
    final static byte TC_STRING = (byte)0x74;
    final static byte TC_ARRAY = (byte)0x75;
    final static byte TC_CLASS = (byte)0x76;
    final static byte TC_BLOCKDATA = (byte)0x77;
    final static byte TC_ENDBLOCKDATA = (byte)0x78;
    final static byte TC_RESET = (byte)0x79;
    final static byte TC_BLOCKDATALONG = (byte)0x7A;
    final static byte TC_EXCEPTION = (byte)0x7B;
    final static byte TC_LONGSTRING = (byte) 0x7C;
    final static byte TC_PROXYCLASSDESC = (byte) 0x7D;
    final static byte TC_ENUM = (byte) 0x7E;    
    final static  int   baseWireHandle = 0x7E0000;
    

    フラグバイト classDescFlags は、次の記号の値を持つことがあります。

         
    final static byte SC_WRITE_METHOD = 0x01; //if SC_SERIALIZABLE     
    final static byte SC_BLOCK_DATA = 0x08;    //if SC_EXTERNALIZABLE
    final static byte SC_SERIALIZABLE = 0x02;
    final static byte SC_EXTERNALIZABLE = 0x04;
    final static byte SC_ENUM = 0x10;
    

    ストリームを書き込んでいる Serializable クラスに writeObject メソッドがあり、それがストリームに追加データを書き込んだ可能性があると、フラグ SC_WRITE_METHOD はオンに設定されます。この場合には、そのクラスのデータは常に TC_ENDBLOCKDATA マーカで終わっていなければなりません。

    STREAM_PROTOCOL_2 を使って Externalizable クラスがストリームに書き込まれる場合は、フラグ SC_BLOCKDATA が設定されます。JDKTM 1.2 のデフォルトでは、このプロトコルを使って Externalizable オブジェクトがストリームに書き込まれます。JDKTM 1.1 では、STREAM_PROTOCOL_1 を使って書き込まれます。

    ストリームを書き込んだクラスが java.io.Serializable を拡張したが、java.io.Externalizable は拡張しなかった場合は、フラグ SC_SERIALIZABLE がオンに設定されます。 そのストリームを読み込むクラスもまた java.io.Serializable を拡張して、デフォルトの直列化機構を使用しなければなりません。

    ストリームを書き込んだクラスが java.io.Externalizable を拡張した場合は、フラグ SC_EXTERNALIZABLE がオンに設定されます。 そのデータを読み込むクラスもまた、 Externalizable を拡張して、デフォルトの直列化機構を使用しなければなりません。 このデータはその writeExternalreadExternal メソッドを使って読み込まれます。

    ストリームに書き込まれたクラスが enum 型であれば、フラグ SC_ENUM が設定されます。受け取り側の対応するクラスも enum 型でなければなりません。enum 型の定数のデータは、「1.12 Enum 定数の直列化」で説明するように、読み書きされます。

    オリジナルクラスと、リンクリストの 2 つのインスタンスの場合を想定します。

     class List implements java.io.Serializable {
    int value;
    List next;
    public static void main(String[] args) {
    try {
    List list1 = new List();
    List list2 = new List();
    list1.value = 17;
    list1.next = list2;
    list2.value = 19;
    list2.next = null;
    
    ByteArrayOutputStream o = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(o);
    out.writeObject(list1);
    out.writeObject(list2);
    out.flush();
                ...
    } catch (Exception ex) {
    ex.printStackTrace();
            }
        }
    }
    

    結果のストリームの内容は次のようになります。

     00: ac ed 00 05 73 72 00 04 4c 69 73 74 69 c8 8a 15 >....sr..Listi...<
     10: 40 16 ae 68 02 00 02 49 00 05 76 61 6c 75 65 4c >Z......I..valueL<
     20: 00 04 6e 65 78 74 74 00 06 4c 4c 69 73 74 3b 78 >..nextt..LList;x<
     30: 70 00 00 00 11 73 71 00 7e 00 00 00 00 00 13 70 >p....sq.~......p<
     40: 71 00 7e 00 03                                  >q.~..<
     
    

    目次 | 前の項目 | 次の項目 Java オブジェクト直列化仕様
    バージョン 5.0

    Copyright © 2004 Sun Microsystems, Inc. All rights reserved