4.1 ObjectStreamClassクラス
ObjectStreamClass
は、直列化ストリームに保存されるクラスの情報を提供します。 この記述子は、クラスの完全修飾名とその直列化バージョンUIDを提供します。 SerialVersionUID
は、このクラスがストリームを書き込んだりストリームから読み込んだりできる、一意のオリジナル・クラス・バージョンを特定します。
package java.io;
public class ObjectStreamClass implements Serializable
{
public static ObjectStreamClass lookup(Class<?> cl);
public static ObjectStreamClass lookupAny(Class<?> cl);
public String getName();
public Class<?> forClass();
public ObjectStreamField[] getFields();
public ObjectStreamField getField(String name);
public long getSerialVersionUID();
public String toString();
}
lookup
メソッドは、仮想マシン内の指定されたクラスのObjectStreamClass
記述子を返します。 クラスにserialVersionUID
が定義されていれば、それがクラスから取り出されます。 serialVersionUID
がクラスによって定義されていなければ、仮想マシン内のクラスの定義から計算されます。 指定されたクラスが直列化可能または外部化可能ではない場合、nullが返されます。
lookupAny
メソッドの動作はlookup
メソッドの動作と似ていますが、Serializable
を実装しているかどうかに関係なくクラスの記述子を返す点が異なります。 Serializable
を実装しないクラスのserialVersionUID
は0Lです。
getName
メソッドが返すクラス名の形式は、Class.getName
メソッドが使用する形式と同じになります。
forClass
メソッドは、ローカル仮想マシン内のClass
(ObjectInputStream.resolveClass
メソッドが検出した場合)を返します。 それ以外の場合は、nullを返します。
getFields
メソッドは、このクラスの直列化可能フィールドを表すObjectStreamField
オブジェクトの配列を返します。
getSerialVersionUID
メソッドは、このクラスのserialVersionUID
を返します。 「セクション4.6、"ストリーム固有の識別子"」を参照してください。 このクラスによって指定されていない場合は、米国国立標準技術研究所によって定義されているSecure Hash Algorithm (SHA)を使って、クラスの名前、インタフェース、メソッド、フィールドから計算されたハッシュ値が返されます。
toString
メソッドは、クラスの名前とserialVersionUID
も含め、クラス記述子の出力可能な表現を返します。
4.2 ダイナミック・プロキシ・クラス記述子
ObjectStreamClass記述子を使って、直列化ストリームに保存されているダイナミック・プロキシ・クラス(java.lang.reflect.ProxyのgetProxyClassメソッドへの呼出しを介して取得されるクラスなど)についての情報を提供することもできます。 ダイナミック・プロキシ・クラス自体は直列化可能フィールドを持たず、0LのserialVersionUIDを持ちます。 つまり、ダイナミック・プロキシ・クラスのClassオブジェクトがObjectStreamClassのstatic lookupメソッドに渡されると、返されるObjectStreamClassインスタンスは、次のプロパティを持ちます。
- getSerialVersionUIDメソッドを呼び出すと、0Lが返されます。
- getFieldsメソッドを呼び出すと、ゼロ長の配列が返されます。
- 任意のString引数でgetFieldメソッドを呼び出すと、nullが返されます。
4.3 直列化された形式
ObjectStreamClassインスタンスの直列化された形式は、その形式が表現するClassオブジェクトが直列化可能であるか、外部化可能であるか、またはダイナミック・プロキシ・クラスであるかによって異なります。
ダイナミック・プロキシ・クラスを表現しないObjectStreamClass
インスタンスがストリームに書き込まれるときには、クラス名とserialVersionUID
、フラグ、およびフィールド数が書き込まれます。 クラスによっては、その他の情報が書き込まれることもあります。
直列化が不可能なクラスは、フィールド数が常にゼロです。
SC_SERIALIZABLE
およびSC_EXTERNALIZABLE
フラグ・ビットは設定されません。直列化可能クラスでは、
SC_SERIALIZABLE
フラグが設定され、フィールド数には直列化可能フィールドの数がカウントされ、そのあとに各直列化可能フィールドの記述子が続きます。 記述子は、正規順序で書き込まれます。 最初に、プリミティブ型フィールドの記述子がフィールド名でソートされて書き込まれ、次に、オブジェクト型フィールドの記述子がフィールド名でソートされて書き込まれます。 名前のソートには、String.compareTo
が使われます。 フォーマットの詳細については、「セクション6.4、"ストリーム形式の文法"」を参照してください。外部化可能クラスでは、フラグは
SC_EXTERNALIZABLE
フラグを含み、フィールド数は常にゼロです。enum型では、フラグは
SC_ENUM
フラグを含み、フィールド数は常にゼロです。
ObjectOutputStreamがダイナミック・プロキシ・クラスのObjectStreamClass記述子を直列化するときは、ダイナミック・プロキシ・クラス(Classオブジェクトをjava.lang.reflect.ProxyのisProxyClassメソッドに渡すことによって決定される)が実装するインタフェース数とインタフェースの名前を書き込みます。 インタフェースは、ダイナミック・プロキシ・クラスのClassオブジェクトでgetInterfacesメソッドを呼び出すことで返される順序に従ってリストされます。
動的プロキシ・クラスと非動的プロキシ・クラスのObjectStreamClass記述子の直列化された表現は、異なる型コード(TC_PROXYCLASSDESC
およびTC_CLASSDESC
、それぞれ)の使用によって区別されます。文法のより詳細な仕様については、「セクション6.4、"ストリーム形式の文法"」を参照してください。
4.4 ObjectStreamFieldクラス
ObjectStreamField
は、直列化可能クラスの直列化可能フィールドを表現します。 クラスの直列化可能フィールドは、ObjectStreamClass
から取得できます。
特別なstatic直列化可能フィールドserialPersistentFields
は、ObjectStreamField
コンポーネントの配列であり、デフォルトの直列化可能フィールドのオーバーライドに使用されます。
package java.io;
public class ObjectStreamField implements Comparable<Object> {
public ObjectStreamField(String fieldName,
Class<?> fieldType);
public ObjectStreamField(String fieldName,
Class<?> fieldType,
boolean unshared);
public String getName();
public Class<?> getType();
public String getTypeString();
public char getTypeCode();
public boolean isPrimitive();
public boolean isUnshared();
public int getOffset();
protected void setOffset(int offset);
public int compareTo(Object obj);
public String toString();
}
ObjectStreamField
オブジェクトは、クラスの直列化可能フィールドを指定したり、ストリームに存在するフィールドを記述したりするために使用されます。 そのコンストラクタは、表現するフィールドを記述する引数を受け取ります: フィールド名を指定する文字列、フィールドの型を指定するClass
オブジェクト、およびboolean
フラグ(2つの引数を持つコンストラクタの暗黙的なfalse
)は、既定の直列化/直列化復元が使用されている場合、表現されたフィールドの値を"unshared"オブジェクトとして読み書きする必要があるかどうかを示します。(それぞれObjectInputStream.readUnshared
およびObjectOutputStream.writeUnshared
メソッドの説明を「セクション3.1、"ObjectInputStreamクラス"」および「セクション2.1、"ObjectOutputStreamクラス"」で参照してください)。
getName
メソッドは、直列化可能フィールドの名前を返します。
getType
メソッドは、フィールドの型を返します。
getTypeString
メソッドは、フィールドの型シグニチャを返します。
getTypeCode
メソッドは、フィールド型の文字エンコードを返します(byte
は'B
' 、char
は'C
'、double
は'D
'、float
の'F
'、int
は'I
'、long
は'J
'、非配列オブジェクト型は'L
'、short
は'S
'、boolean
は'Z
'、および配列は'[
)。
isPrimitive
メソッドは、フィールドがプリミティブ型の場合はtrue
を返し、それ以外の場合はfalse
を返します。
isUnshared
メソッドは、フィールドの値を非共有オブジェクトとして書き込むべき場合はtrue
を返し、それ以外の場合はfalse
を返します。
getOffset
メソッドは、フィールドを定義するクラスのインスタンス・データ内でのフィールド値のオフセットを返します。
setOffset
メソッドは、getOffset
メソッドから返されたオフセット値をObjectStreamField
サブクラスで変更できるようにします。
compareTo
メソッドは、ソートに使用するためにObjectStreamFields
を比較します。 プリミティブ・フィールドは、非プリミティブ・フィールドよりも「小さい」順位にランク付けられます。等しいフィールドは、アルファベット順にランク付けられます。
toString
メソッドは、名前と型による出力可能な表現を返します。
4.5 直列化可能クラスの検査
プログラムserialverを使うと、クラスが直列化可能かどうかを判断し、そのserialVersionUID
を取得できます。
1つまたは複数のクラス名付きでコマンド行から呼び出されたserialverは、展開中のクラスにコピーするのに適した形式で各クラスのserialVersionUID
を出力します。 引数が指定されていないと、このプログラムの使用方法が出力されます。
4.6 ストリーム固有識別子
バージョン管理された各クラスは、ストリームを書き込んだりストリームから読み込んだりできる、オリジナル・クラス・バージョンを識別する必要があります。 たとえば、バージョン管理されたクラスは、次のように宣言する必要があります。
private static final long serialVersionUID = 3487495895819393L;
ストリーム固有識別子は、クラス名、インタフェース・クラス名、メソッド、およびフィールドの64ビット・ハッシュです。 最初のバージョンを除くクラスのすべてのバージョンで、この値を宣言する必要があります。 この値は、オリジナル・クラスに宣言することもできますが、必須ではありません。 互換性のあるすべてのクラスで、この値は一定です。 クラスのSUIDを宣言しない場合は、値はデフォルトでそのクラスのハッシュになります。 ダイナミック・プロキシ・クラスおよびenum型のserialVersionUID
は常に、値0Lになります。 配列クラスは明示的なserialVersionUID
を宣言できないため、常にデフォルト計算値を持ちますが、配列クラスに関してはserialVersionUID
値の一致要件は適用されません。 レコード・クラスは、0L
のデフォルト値serialVersionUID
を持ちますが、明示的なserialVersionUID
を宣言できます。 レコード・クラスについてのserialVersionUID
値の照合に関する要件は免除されます。
ノート: デフォルトのserialVersionUID
計算は、コンパイラの実装によって異なる可能性のあるクラスの詳細に対して非常にセンシティブであり、したがって直列化復元中に予期しないserialVersionUID
の競合が発生し、直列化復元が失敗する可能性があるため、すべての直列化可能クラスがserialVersionUID
値を明示的に宣言することを強く推奨します。
Externalizable
クラスの初期バージョンでは、将来的に拡張可能なストリーム・データ形式を出力する必要があります。 readExternal
メソッドの初期バージョンは、writeExternal
メソッドの将来のすべてのバージョンの出力形式を読取り可能でなければいけません。
serialVersionUID
は、クラス定義を反映したバイト・ストリームのシグネチャを使用して計算されます。 ストリームのシグネチャの計算には、米国国立標準技術研究所(NIST)のSecure Hash Algorithm (SHA-1)が使用されます。 64ビット・ハッシュには、最初の2つの32ビット数が使用されます。 プリミティブ・データ型からバイト・シーケンスへの変換には、java.lang.DataOutputStream
が使用されます。 ストリームに入力される値は、クラスのJava仮想マシン(VM)仕様によって定義されます。 クラス修飾子にはACC_PUBLIC
、ACC_FINAL
、ACC_INTERFACE
、ACC_ABSTRACT
フラグを含めることができます。その他のフラグは無視され、serialVersionUID
の計算に影響しません。 同様に、フィールド修飾子では、ACC_PUBLIC
、ACC_PRIVATE
、ACC_PROTECTED
、ACC_STATIC
、ACC_FINAL
、ACC_VOLATILE
、ACC_TRANSIENT
フラグのみがserialVersionUID
値の計算に使用されます。 コンストラクタおよびメソッド修飾子では、ACC_PUBLIC
、ACC_PRIVATE
、ACC_PROTECTED
、ACC_STATIC
、ACC_FINAL
、ACC_SYNCHRONIZED
、ACC_NATIVE
、ACC_ABSTRACT
、ACC_STRICT
フラグのみが使用されます。 名前と記述子は、java.io.DataOutputStream.writeUTF
メソッドが使用する形式で書き込まれます。
ストリーム内の項目の順序は次のとおりです。
クラス名。
クラス修飾子(32ビット整数として書き込まれる)。
各インタフェース名(名前でソート)。
フィールド名でソートされたクラスの各フィールドの場合(
private static
およびprivate transient
フィールドを除く):フィールドの名前。
フィールドの修飾子(32ビット整数として書き込まれる)。
フィールドの記述子。
クラス初期化子が存在する場合は、以下を書き出してください。
メソッドの名前、
<clinit>
。メソッドの修飾子、
java.lang.reflect.Modifier.STATIC
、32ビット整数として書き込まれる。メソッドの記述子、
()V
。
メソッド名とシグニチャでソートされた
private
でない各コンストラクタの場合:メソッドの名前、
<init>
。メソッドの修飾子(32ビット整数として書き込まれる)。
メソッドの記述子。
メソッド名とシグニチャでソートされた
private
でない各メソッドの場合:メソッドの名前。
メソッドの修飾子(32ビット整数として書き込まれる)。
メソッドの記述子。
SHA-1アルゴリズムは、
DataOutputStream
によって作成されたバイト・ストリームに対して実行され、5つの32ビット値からなるsha[0..4]
を作成します。ハッシュ値は、SHA-1メッセージ・ダイジェストの1つ目と2つ目の32ビット値で組み立てられます。 メッセージ・ダイジェストの結果である32ビットの5つの語
H0 H1 H2 H3 H4
は、sha
という名前の5つのint
値の配列であり、ハッシュ値は次のように計算されます。
long hash = ((sha[0] >>> 24) & 0xFF) |
((sha[0] >>> 16) & 0xFF) << 8 |
((sha[0] >>> 8) & 0xFF) << 16 |
((sha[0] >>> 0) & 0xFF) << 24 |
((sha[1] >>> 24) & 0xFF) << 32 |
((sha[1] >>> 16) & 0xFF) << 40 |
((sha[1] >>> 8) & 0xFF) << 48 |
((sha[1] >>> 0) & 0xFF) << 56;