目次|前|次 | 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.~..<