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

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






第 6 章

トピック:


6.1 概要

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


6.2 ストリーム要素

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

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

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

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

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 の書き込みに使用された形式を表しています。

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

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

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

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


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

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

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

  初期のストリーム形式を示します。
  新しい外部データ形式を示します。プリミティブデータはブロックデータモードで書き込まれ、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.~..<





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