| 目次 | 前へ | 次へ | Java オブジェクト直列化仕様 Version 6.0 |
|
第 6 章 |
トピック:
このストリーム形式は、次の設計目標を実現しました。
クラスオブジェクトは、次の要素によって表されます。
ダイナミックプロキシクラス以外の Class の ObjectStreamClass オブジェクトは、次の要素によって表されます。
writeObject メソッドを定義しているか、クラスが直列化可能、外部化可能、または enum 型か、など)。
Ljava/lang/Object; など) でなければいけない) として含まれる。
annotateClass メソッドによって書き込まれるオプションのブロックデータレコードまたはオブジェクト
ObjectStreamClass (スーパークラスが直列化可能でなければ null)
ダイナミックプロキシクラスの ObjectStreamClass オブジェクトは、次の要素によって表されます。
getInterfaces メソッドを呼び出すことで返される順番にリストする。
annotateProxyClass メソッドによって書き込まれるオプションのブロックデータレコードまたはオブジェクト。
java.lang.reflect.Proxy の ObjectStreamClass。
String オブジェクトの表現は、長さ情報および Modified UTF-8 でエンコードされた文字列の内容で構成されます。Modified UTF-8 エンコーディングは、Java 仮想マシンと java.io.DataInput および DataOutput インタフェースで使用されるものと同じですが、補助文字と null 文字の表現が標準 UTF-8 とは異なります。長さ情報の形式は、Modified UTF-8 エンコーディング内の文字列の長さに依存します。指定された String の Modified UTF-8 エンコーディング長が 65,536 バイト未満の場合、長さは符号なし 16 ビット整数を表す 2 バイトとして書き込まれます。Java 2 platform, Standard Edition, v1.3 以降では、Modified UTF-8 エンコーディングでの文字列の長さが 65,536 バイト以上の場合、長さは符号付き 64 ビット整数を表す 8 バイトで書き込まれます。直列化ストリーム内で String の前にある型コードは、String の書き込みに使用された形式を表しています。配列は次の要素によって表されます。
enum 定数は次の要素によって表されます。
ストリーム内の新規オブジェクトは次の要素によって表されます。
writeObject/readObject メソッドを持つ場合、writeObject メソッドによって書き込まれたプリミティブ型のオプションオブジェクトまたはブロックデータレコード、あるいはその両方と、endBlockData がある場合があります。クラスによって書き込まれたすべてのプリミティブデータは、バッファリングされてブロックデータレコードにラップされます (データが writeObject メソッド内でストリームに書き込まれたか、writeObject メソッドの外部から直接ストリームに書き込まれたのかに関係なく)。このデータは、対応する readObject メソッドによって読み込むか、ストリームから直接読み込むことができます。writeObject メソッドによって書き込まれるオブジェクトは、既存のブロックデータレコードを終了し、必要に応じて通常のオブジェクト、null、または逆参照として書き込まれます。ブロックデータレコードでは、エラー回復によってオプションデータを破棄できます。クラス内から呼び出された場合には、ストリームはデータやオブジェクトを endBlockData まで破棄できます。
ObjectOutputStream.useProtocolVersion メソッドは、直列化ストリームを書き込むときに使用するプロトコルバージョンをパラメータとしてとります。ストリームプロトコルバージョンを次に示します。
ObjectStreamConstants.PROTOCOL_VERSION_1: 初期ストリーム形式を示します。
ObjectStreamConstants.PROTOCOL_VERSION_2: 新しい外部データ形式を示します。プリミティブデータはブロックデータモードで書き込まれ、TC_ENDBLOCKDATA で終了します。
ブロックデータ境界が標準化されました。ブロックデータモードで書き込まれたプリミティブデータは、1024 バイトチャンクを超えないように正規化されます。この変更の利点は、ストリーム内の直列化データ形式の仕様を厳しくすることでした。この変更は、完全に下位および上位互換です。
PROTOCOL_VERSION_2 を書き込みます。
JDK 1.1 は、デフォルトで PROTOCOL_VERSION_1 を書き込みます。
JDK 1.1.7 以降は、両方のバージョンを読み取ることができます。
JDK 1.1.7 より前のリリースは、PROTOCOL_VERSION_1 のみを読み取れます。
以下の表は、ストリーム形式の文法を示しています。非ターミナルシンボルは、イタリックで示されます。ターミナルシンボルは固定幅フォントで示されます。非ターミナルの定義は、その後に「:」が続きます。定義のあとには 1 つまたは複数の代替定義が続き、それぞれが別の行に示されます。次の表に表記法を示します。
| 表記法 | 意味 |
|
(データ型) |
このトークンには byte などのデータ型が指定される。 |
|
トークン[n] |
このトークンの事前定義オカレンス数 (配列)。 |
|
x0001 |
16 進数で表したリテラル値。16 進数の数字が値のサイズを表す。 |
|
<xxx> |
配列の長さを示すために使用される、ストリームから読み込まれた値。 |
シンボル (utf) は、2 バイトの長さ情報を使用して書き込まれた文字列を示すために使用され、(long-utf) は、8 バイトの長さ情報を使用して書き込まれた文字列を示すために使用されます。詳細については、セクション 6.2「ストリーム要素」を参照してください。
直列化されたストリームは、ストリーム規則を満たすストリームによって表されます。
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
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;
フラグ byte 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;
フラグ SC_WRITE_METHOD は、ストリームを書き込んでいる Serializable クラスに writeObject メソッドがあり、それがストリームに追加データを書き込んだ可能性がある場合に設定されます。この場合、TC_ENDBLOCKDATA マーカーが常にそのクラスのデータを終了することが期待されます。
フラグ SC_BLOCKDATA は、STREAM_PROTOCOL_2 を使って Externalizable クラスがストリームに書き込まれる場合に設定されます。デフォルトでは、これが JDK 1.2 で Externalizable オブジェクトをストリームに書き込むために使用されるプロトコルです。JDK 1.1 は、STREAM_PROTOCOL_1 を書き込みます。
フラグ SC_SERIALIZABLE は、ストリームを書き込んだクラスが java.io.Serializable を拡張したけれども、java.io.Externalizable は拡張しなかった場合に設定されます。そのストリームを読み込むクラスも java.io.Serializable を拡張してデフォルト直列化メカニズムを使用しなければいけません。
フラグ SC_EXTERNALIZABLE は、ストリームを書き込んだクラスが java.io.Externalizable を拡張した場合に設定されます。そのデータを読み込むクラスも Externalizable を拡張する必要があり、データはその writeExternal および readExternal メソッドを使って読み込まれます。
フラグ SC_ENUM は、ストリームを書き込んだクラスが enum 型だった場合に設定されます。受け取り側の対応するクラスも enum 型でなければいけません。enum 型の定数のデータは、セクション 1.12「Enum 定数の直列化」で説明するように読み書きされます。
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.~..<