目次 | 前へ | 次へ Java オブジェクト直列化仕様
Version 6.0

クラス記述子






第 4 章

トピック:

4.1 ObjectStreamClass クラス

ObjectStreamClass は、直列化ストリームに保存されるクラスの情報を提供します。この記述子は、クラスの完全修飾名とその直列化バージョン UID を提供します。SerialVersionUID は、このクラスがストリームを書き込んだりストリームから読み込んだりできる、一意のオリジナルクラスバージョンを特定します。

package java.io;

public class ObjectStreamClass
{
    public static ObjectStreamClass lookup(Class cl);

        public static ObjectStreamClass lookupAny(Class cl);

    public String getName();

    public Class forClass();

    public ObjectStreamField[] getFields();

    public long getSerialVersionUID();

    public String toString();
}
lookup メソッドは、仮想マシン内の指定されたクラスの ObjectStreamClass 記述子を返します。クラスに serialVersionUID が定義されていれば、それがクラスから取り出されます。serialVersionUID がクラスによって定義されていなければ、仮想マシン内のクラスの定義から計算されます。指定されたクラスが Serializable でも Externalizable でもない場合、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 インスタンスは、次のプロパティーを持ちます。


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 {

    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) があります (セクション 3.1 および 2.1 の ObjectInputStream.readUnshared メソッドおよび ObjectOutputStream.writeUnshared メソッドの説明を参照)。

getName メソッドは、直列化可能フィールドの名前を返します。

getType メソッドは、フィールドの型を返します。

getTypeString メソッドは、フィールドの型シグニチャーを返します。

getTypeCode メソッドは、フィールド型の文字エンコーディングを返します ('B' は byte、'C' は char、'D' は double、'F' は float、'I' は int、'J' は long、'L' は非配列オブジェクト型、'S' は short、'Z' は boolean、'[' は配列)。

isPrimitive メソッドは、フィールドがプリミティブ型の場合は true を返し、それ以外の場合は false を返します。

isUnshared メソッドは、フィールドの値を非共有オブジェクトとして書き込むべき場合は true を返し、それ以外の場合は false を返します。

getOffset メソッドは、フィールドを定義するクラスのインスタンスデータ内でのフィールド値のオフセットを返します。

setOffset メソッドは、getOffset メソッドから返されたオフセット値を ObjectStreamField サブクラスで変更できるようにします。

compareTo メソッドは、ソートに使うために ObjectStreamFields を比較します。プリミティブフィールドは、非プリミティブフィールドよりも「小さい」順位にランク付けられます。等しいフィールドは、アルファベット順にランク付けられます。

toString メソッドは、名前と型による出力可能な表現を返します。


4.5 直列化可能クラスの検査

プログラム serialver を使うと、クラスが直列化可能かどうかを判断し、その serialVersionUID を取得できます。-show オプションを指定して呼び出すと、このプログラムは簡単なユーザーインタフェースを表示します。クラスが直列化可能かどうかを判断し、その serialVersionUID, を取得するには、クラス名全体を指定し、Enter キーまたは「Show」ボタンを押します。出力された文字列は、コピーして、展開されたクラスにペーストできます。

serialver、シリアルバージョンインスペクタプログラム

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_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 メソッドが使用する形式で書き込まれます。

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

  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;
                             
    


目次 | 前へ | 次へ
Copyright © 2005, 2010, Oracle and/or its affiliates. All rights reserved.