目次 | 前の項目 | 次の項目 Java オブジェクト直列化仕様
バージョン 5.0


クラス記述子

トピック:

  • ObjectStreamClass クラス
  • ダイナミックプロキシクラス記述子
  • 直列化された形式
  • ObjectStreamField クラス
  • 直列化可能クラスの検査
  • ストリーム固有識別子

    4.1 ObjectStreamClass クラス

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

     package java.io;
    
    public class ObjectStreamClass
    {
    public static ObjectStreamClass lookup(Class cl);
    
    public String getName();
    
    public Class forClass();
    
    public ObjectStreamField[] getFields();
    
    public long getSerialVersionUID();
    
    public String toString();
    }
    

    lookup メソッドは、仮想マシンの指定されたクラスに対する ObjectStreamClass 記述子を返します。このクラスに serialVersionUID が定義されていれば、それがこのクラスから取り出されます。定義されていなければ、仮想マシンにあるこのクラスの定義から計算されます。指定されたクラスが直列化可能でも外部化可能でもない場合、null が返されます。

    getName メソッドは、そのクラスの完全修飾名を返します。このクラス名はストリームに保管され、そのクラスがロードされるときに使われます。

    forClass メソッドは、ObjectInputStream.resolveClass メソッドがローカルの仮想マシン内に Class を検出した場合に、その Class を返します。それ以外の場合は、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 から取得できます。

    特別な静的直列化可能フィールド (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 つまたは複数のクラス名が指定されていると、展開中のクラスにそれぞれのクラスの serialVersionUID をコピーできるような形式で表示します。引数が指定されていないと、このプログラムの使用方法が表示されます。

    4.6 ストリーム固有識別子

    バージョン管理された各クラスでは、オリジナルクラスバージョンを指定する必要があり、それによってストリームの書き込み、読み込みが可能になります。たとえば、バージョン管理されたクラスは、次のように宣言する必要があります。

         private static final long serialVersionUID = 3487495895819393L;
    

    ストリーム固有識別子は、クラス名、インタフェースクラス名、メソッド、およびフィールドをハッシュした 64 ビットの値です。最初のバージョンを除くクラスのすべてのバージョンで、この値を宣言する必要があります。この値は、オリジナルクラスに宣言することもできますが、必須ではありません。互換性のあるすべてのクラスで、この値は一定です。クラスに対してこの SUID を宣言しないと、この値はそのクラスのハッシュになります。ダイナミックプロキシクラスおよび enum 型の serialVersionUID は、常に値 0L になります。


    注 - すべての直列化可能なクラスは、serialVersionUID の値を明示的に宣言することを強くお勧めします。これは、デフォルトの serialVersionUID の算出は、コンパイラ実装により異なることがあるクラスの詳細によって大きく変わり、そのため直列化復元中に serialVersionUID の予期しない競合が発生して直列化復元が失敗する可能性があるためです。

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

    serialVersionUID は、そのクラス定義を反映したバイトストリームのシグニチャーを使って計算されます。ストリームのシグニチャーの計算には、米国国立標準技術研究所 (NIST) の Secure Hash Algorithm (SHA-1) が使用されます。64 ビットハッシュには、最初の 2 つの 32 ビットが使われます。プリミティブデータ型からバイト列への変換には、java.lang.DataOutputStream が使用されます。このストリームに入力される値は、クラスに対する JavaTM 仮想マシン (VM) の指定によって定義されます。クラスの修飾子には ACC_PUBLICACC_FINALACC_INTERFACE、および ACC_ABSTRACT フラグがあります。その他のフラグは無視され、serialVersionUID の計算に影響しません。同様にフィールドの修飾子では、ACC_PUBLICACC_PRIVATEACC_PROTECTEDACC_STATICACC_FINALACC_VOLATILE、および ACC_TRANSIENT フラグだけが serialVersionUID の値の計算に使用されます。コンストラクタおよびメソッドの修飾子の場合は、ACC_PUBLICACC_PRIVATEACC_PROTECTEDACC_STATICACC_FINALACC_SYNCHRONIZEDACC_NATIVEACC_ABSTRACT、および ACC_STRICT フラグだけが使用されます。名前と記述子は、java.io.DataOutputStream.writeUTF メソッドが使用する形式で記述されます。

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

    1. クラス名

    2. 32 ビット整数として書かれたクラス修飾子

    3. 名前によってソートされた各インタフェース名

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


      1. フィールドの名前
      2. 32 ビット整数として書かれたフィールドの修飾子
      3. フィールドの記述子

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


      1. メソッドの名前 <clinit>
      2. 32 ビット整数として書かれたメソッドの修飾子 java.lang.reflect.Modifier.STATIC
      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;


    目次 | 前の項目 | 次の項目 Java オブジェクト直列化仕様
    バージョン 5.0

    Copyright © 2004 Sun Microsystems, Inc. All rights reserved