このストリーム形式は、次の設計目標を実現しました。
基本構造は、ストリーム内のオブジェクトを表す必要があります。 オブジェクトの各属性(クラス、フィールド、およびクラス固有のメソッドによって書き込まれてあとで読み取られるデータ)を表現する必要があります。 ストリーム内のオブジェクトの表現は、文法で記述できます。 nullオブジェクト、新規オブジェクト、クラス、配列、文字列、およびストリーム内の既存のオブジェクトへの逆参照には、特別な表現があります。 ストリームに書き込まれた各オブジェクトには、オブジェクトに逆参照するために使用するハンドルが割り当てられます。 ハンドルは、0x7E0000からシーケンシャルに割り当てられます。 ストリームがリセットされると、ハンドルはふたたび0x7E0000から始まります。
クラス・オブジェクトは、次の要素によって表されます。
ObjectStreamClass
オブジェクト。ダイナミック・プロキシ・クラス以外のClassのObjectStreamClass
オブジェクトは、次の要素によって表されます。
互換クラスのストリーム固有識別子(SUID)。
クラスのさまざまなプロパティを表すフラグのセット(クラスがwriteObject
メソッドを定義しているか、クラスが直列化可能、外部化可能、またはenum型か、など)。
直列化可能フィールドの数
デフォルトのメカニズムによって直列化された、クラスのフィールドの配列。配列やオブジェクト・フィールドの場合、フィールドの型が文字列(Java仮想マシン仕様に指定されているフィールド記述子形式(Ljava/lang/Object;
など)でなければいけない)として含まれる。
annotateClass
メソッドによって書き込まれるオプションのブロック・データ・レコードまたはオブジェクト
スーパー・タイプのObjectStreamClass
(スーパー・クラスが直列化可能でなければnull)
ダイナミック・プロキシ・クラスのObjectStreamClass
オブジェクトは、次の要素によって表されます。
ダイナミック・プロキシ・クラスが実装するインタフェース数
ダイナミック・プロキシ・クラスが実装するすべてのインタフェースの名前。そのClassオブジェクト上で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
の書込みに使用された形式を表しています。
配列は次の要素によって表されます。
そのObjectStreamClass
オブジェクト。
要素数。
値のシーケンス。 値の型は配列の型で暗示されます。たとえば、byte配列の値の型はbyteです。
enum定数は次の要素によって表されます。
定数の基底enum型のObjectStreamClass
オブジェクト。
定数の名前文字列。
ストリーム内の新規オブジェクトは次の要素によって表されます。
オブジェクトのもっとも多く派生されたクラス。
オブジェクトの各直列化可能クラスのデータ(最上位のスーパー・クラスから順に)。 各クラスについて、ストリームは次のものを含みます。
直列化可能なフィールド。「セクション1.5、"クラスの直列化可能フィールドの定義"」を参照してください。
クラスがwriteObject
/readObject
メソッドを持つ場合、writeObject
メソッドによって書き込まれたプリミティブ型のオプション・オブジェクトまたはブロック・データ・レコード、あるいはその両方と、endBlockData
がある場合があります。
クラスによって書き込まれたすべてのプリミティブ・データは、バッファリングされてブロック・データ・レコードにラップされます(データがwriteObject
メソッド内でストリームに書き込まれたか、writeObject
メソッドの外部から直接ストリームに書き込まれたのかに関係なく)。 このデータは、対応するreadObject
メソッドによって読み込むか、ストリームから直接読み込むことができます。 writeObject
メソッドによって書き込まれるオブジェクトは、既存のブロック・データ・レコードを終了し、必要に応じて通常のオブジェクト、null、または逆参照として書き込まれます。 ブロック・データ・レコードでは、エラー回復によってオプション・データを破棄できます。 クラス内から呼び出された場合には、ストリームはデータやオブジェクトをendBlockData
まで破棄できます。
JDK 1.2での直列化ストリーム形式への変更は、JDK 1.1のすべてのマイナー・リリースと下位互換性のない方法で行う必要がありました。 下位互換性が必要な場合に備えるために、直列化ストリームを書き込むときにどのPROTOCOL_VERSION
を使用するかを示す機能が追加されました。 ObjectOutputStream.useProtocolVersion
メソッドは、直列化ストリームを書き込むときに使用するプロトコル・バージョンをパラメータとしてとります。
ストリーム・プロトコル・バージョンを次に示します。
ObjectStreamConstants.PROTOCOL_VERSION_1
: 初期ストリーム形式を示します。
ObjectStreamConstants.PROTOCOL_VERSION_2
: 新しい外部データ形式を示します。 プリミティブ・データはブロック・データ・モードで書き込まれ、TC_ENDBLOCKDATA
で終了します。
ブロック・データ境界が標準化されました。 ブロック・データ・モードで書き込まれたプリミティブ・データは、1024バイト・チャンクを超えないように正規化されます。 この変更の利点は、ストリーム内の直列化データ形式の仕様を厳しくすることでした。 この変更は、完全に下位および上位互換です。
JDK 1.2は、デフォルトでPROTOCOL_VERSION_2
を書き込みます。
JDK 1.1は、デフォルトでPROTOCOL_VERSION_1
を書き込みます。
JDK 1.1.7以降は、両方のバージョンを読み取ることができます。
JDK 1.1.7より前のリリースは、PROTOCOL_VERSION_1
のみを読み取れます。
以下の表は、ストリーム形式の文法を示しています。 非ターミナル・シンボルは、イタリックで示されます。 ターミナル・シンボルは固定幅フォントで示されます。 非ターミナルの定義は、その後に:が続きます。 定義のあとには1つまたは複数の代替定義が続き、それぞれが別の行に示されます。 次の表に表記法を示します。
Notation | 意味 |
---|---|
(datatype) | このトークンにはbyteなどのデータ型が指定される。 |
token [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型でなければいけません。 列挙型の定数のデータは、「セクション1.12、"列挙型定数の直列化"」で説明されているように書き込まれ、読み込まれます。
オリジナル・クラスと、リンクされたリスト内の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, 2017, Oracle and/or its affiliates. All rights reserved.
目次|前|次