Javaオブジェクト直列化仕様: 4 - クラス記述子

機械翻訳について

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を実装しないクラスのserialVersionUID0Lです。

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インスタンスは、次のプロパティを持ちます。

4.3 直列化された形式

ObjectStreamClassインスタンスの直列化された形式は、その形式が表現するClassオブジェクトが直列化可能であるか、外部化可能であるか、またはダイナミック・プロキシ・クラスであるかによって異なります。

ダイナミック・プロキシ・クラスを表現しないObjectStreamClassインスタンスがストリームに書き込まれるときには、クラス名とserialVersionUID、フラグ、およびフィールド数が書き込まれます。 クラスによっては、その他の情報が書き込まれることもあります。

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値の一致要件は適用されません。

ノート: デフォルトのserialVersionUID計算は、コンパイラの実装によって異なる可能性のあるクラスの詳細に対して非常にセンシティブであり、したがって直列化復元中に予期しないserialVersionUIDの競合が発生し、直列化復元が失敗する可能性があるため、すべての直列化可能クラスがserialVersionUID値を明示的に宣言することを強く推奨します。

Externalizableクラスの初期バージョンでは、将来的に拡張可能なストリーム・データ形式を出力する必要があります。 readExternalメソッドの初期バージョンは、writeExternalメソッドの将来のすべてのバージョンの出力形式を読取り可能でなければいけません。

serialVersionUIDは、クラス定義を反映したバイト・ストリームのシグネチャを使用して計算されます。 ストリームのシグネチャの計算には、米国国立標準技術研究所(NIST)のSecure Hash Algorithm (SHA-1)が使用されます。 64ビット・ハッシュには、最初の2つの32ビット数が使用されます。 プリミティブ・データ型からバイト・シーケンスへの変換には、java.lang.DataOutputStreamが使用されます。 ストリームに入力される値は、クラスのJava仮想マシン(VM)仕様によって定義されます。 クラス修飾子にはACC_PUBLICACC_FINALACC_INTERFACEACC_ABSTRACTフラグを含めることができます。その他のフラグは無視され、serialVersionUIDの計算に影響しません。 同様に、フィールド修飾子では、ACC_PUBLICACC_PRIVATEACC_PROTECTEDACC_STATICACC_FINALACC_VOLATILEACC_TRANSIENTフラグのみがserialVersionUID値の計算に使用されます。 コンストラクタおよびメソッド修飾子では、ACC_PUBLICACC_PRIVATEACC_PROTECTEDACC_STATICACC_FINALACC_SYNCHRONIZEDACC_NATIVEACC_ABSTRACTACC_STRICTフラグのみが使用されます。 名前と記述子は、java.io.DataOutputStream.writeUTFメソッドが使用する形式で書き込まれます。

ストリーム内の項目の順序は次のとおりです。

  1. クラス名。

  2. クラス修飾子(32ビット整数として書き込まれる)。

  3. 各インタフェース名(名前でソート)。

  4. フィールド名でソートされたクラスの各フィールドの場合(private staticおよびprivate transientフィールドを除く):

    1. フィールドの名前。

    2. フィールドの修飾子(32ビット整数として書き込まれる)。

    3. フィールドの記述子。

  5. クラス初期化子が存在する場合は、以下を書き出してください。

    1. メソッドの名前、<clinit>

    2. メソッドの修飾子、java.lang.reflect.Modifier.STATIC、32ビット整数として書き込まれる。

    3. メソッドの記述子、()V

  6. メソッド名とシグニチャでソートされたprivateでない各コンストラクタの場合:

    1. メソッドの名前、<init>

    2. メソッドの修飾子(32ビット整数として書き込まれる)。

    3. メソッドの記述子。

  7. メソッド名とシグニチャでソートされたprivateでない各メソッドの場合:

    1. メソッドの名前。

    2. メソッドの修飾子(32ビット整数として書き込まれる)。

    3. メソッドの記述子。

  8. SHA-1アルゴリズムは、DataOutputStreamによって作成されたバイト・ストリームに対して実行され、5つの32ビット値からなるsha[0..4]を作成します。

  9. ハッシュ値は、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;