Java 

以前のリリースでの
オブジェクト直列化の拡張機能

ドキュメントの目次
非共有オブジェクトの直列化復元のサポート (1.4 以降)
データ直列化ストリームで非共有オブジェクトの直列化復元も、直列化でサポートするようになりました。この新しいサポートは、パッケージ java.io に次のような API を追加することにより提供されます。
ObjectInputStream.readUnshared()

ObjectOutputStream.writeUnshared(Object obj)

ObjectStreamField(String name, Class type, boolean unshared)

以前は、セキュリティを重視するプログラマは、非公開内部オブジェクトの直列化復元を行なったあと、それらを複製する必要がありました。これは、直列化ストリームへのアクセス権を持つ外部パーティが、機密オブジェクトに偽のバックハンドルを付加することにより、直列化復元中に外部からの参照が可能になることを避けるためです。ただし、このようにするとパフォーマンスが低下しメモリを浪費します。つまり、単一の使用可能なオブジェクトへの一意の参照を確保するために、オブジェクトを 2 つ作成し、複製操作を呼び出す必要があります。新しく追加された API ではより効率的な解決法が提供されています。受信側で非共有オブジェクトを読み取って無効にし、サードパーティが以後のストリームで発生するオブジェクトのバックハンドルを間接参照できないようにします。

putFields、readFields のオーバーライドに必要なセキュリティアクセス権 (1.4 以降)
J2SE 1.4.0 から、ObjectOutputStream.putFields または ObjectOutputStream.writeUnshared をオーバーライドするサブクラスで ObjectOutputStream の 引数を 1 つ取る public コンストラクタを直接的または間接的に呼び出す場合、"enableSubclassImplementation" SerializablePermission が必要になりました。

同様に、J2SE 1.4.0 から、ObjectInputStream.putFields または ObjectInputStream.writeUnshared をオーバーライドするサブクラスで ObjectInputStream の 引数を 1 つ取る public コンストラクタ を直接的または間接的に呼び出す場合にも、"enableSubclassImplementation" SerializablePermission が必要になりました。

このような変更は、アプリケーションの大部分には影響がありません。ただし、putFields または readFields メソッドをオーバーライドするが直列化インフラストラクチャの残りの部分はオーバーライドしない ObjectInputStream サブクラスや ObjectOutputStream サブクラスは影響を受けます。

クラス定義メソッド readObjectNoData のサポート (1.4 以降)
クラス定義メソッドの writeObject()readObject() のほかにも、直列化によってクラス定義メソッドの readObjectNoData() がサポートされます。それぞれのクラス定義メソッド readObjectNoData() には、次のシグニチャーが必要です。
private void readObjectNoData() throws ObjectStreamException;
readObjectNoData() はクラス定義メソッド readObject() と似ています。ただし、定義されている場合は、直列化復元中のオブジェクトのスーパークラスのクラス記述子およびそのクラス記述子で記述されるオブジェクトデータが、直列化ストリームにない場合に呼び出されるという点が異なります。つまり、以下のとおりです。
クラス C のオブジェクト O が直列化復元中で、O を直列化復元している VM 内の C のスーパークラスが S である場合、O の直列復元中に ObjectInputStream によって S.readObjectNoData() が呼び出されるのは、次の条件が成立する場合だけです。
  1. S が java.io.Serializable を直接的または間接的に実装する
  2. S が前述のシグニチャーを使って readObjectNoData() メソッドを定義する
  3. O を含む直列化ストリームが、C のスーパークラス記述子のリストに S のクラス記述子を含まない
クラス定義メソッド readObject() の呼び出しが可能な場合は、readObjectNoData() が呼び出されることはありません。ただし、直列化可能クラスの実装を行う際に、初期化コードをまとめる手段として、readObject() から readObjectNoData() を呼び出すことができます。

詳細は、ObjectInputStream の API 仕様でクラス記述子を参照してください。

バグの修正:プリミティブ型の Class オブジェクトの直列化復元エラー (1.4 以降)
以前のリリースでは、プリミティブ型の Class オブジェクトの直列化復元をしようとすると ClassNotFoundException エラーになりました (バグ 4171142)。これは、プリミティブ型の ObjectStreamClass 記述子には ObjectInputStream.resolveClass() を使用できないためです。J2SE 1.4.0 ではこのバグは修正されました。

バグの修正:public 以外のインタフェースで ObjectInputStream.resolveProxyClass がエラーになることがある (1.4 以降)
以前のリリースでは、1 つまたは複数のプロキシインタフェースが public でない場合、ObjectInputStream.resolveProxyClass はプロキシクラスを定義するクラスローダを正しく選択するとは限りませんでした。このリリースでは、ObjectInputStream.resolveProxyClass が public 以外のインタフェースを検出すると、実装するプロキシクラスをインタフェースと同じクラスローダに定義しようとし、競合する場合は例外をスローします。これは、プロキシがインタフェースを実装するために必要です。

バグの修正:無効な serialPersistentFields のフィールド名による NullPointerException の発生 (1.4 以降)
以前のリリースでは、デフォルトの直列化を使用するが実際のクラスフィールドにマップされていない serialPersistentField エントリを宣言するオブジェクトを直列化すると、NullPointerExceptions がスローされました (バグ 4387368)。このリリースでは、そのような場合、直列化は InvalidClassExceptions をスローします。デフォルトの直列化を使用するのであれば、そのような「サポートされない」serialPersistentFields を定義する必要はないからです。

バグの修正: スキップされたオブジェクトでの ClassNotFoundException による直列化エラー (1.4 以降)
以前のリリースでは、「スキップされた」オブジェクト (直列化復元パーティによってロードされたクラスにないフィールドに関連付けられたオブジェクト) により ClassNotFoundExceptions が発生すると、オブジェクトグラフ全体の直列化復元エラーとなりました。これは、スキップされた値がグラフに含まれていない場合も同様でした。今回のリリースの直列化では、このようなスキップされたオブジェクトに関連付けられた ClassNotFoundExceptions を無視し、不要な直列化復元エラーのクラスを排除することで問題に対処しています。直列化復元中に発生する ClassNotFoundExceptions に関連して、直列化全体を堅牢にするための変更がこのほかにも行われています。
64K を超える文字列を直列化できる (1.3 以降)
1.3 より前は、64K を超える文字列を直列化しようとすると、java.io.UTFDataFormatException がスローされました。1.3 では、64K を超える文字列を直列化できるように、直列化プロトコルが拡張されました。ただし、1.2 以前の JVM で、1.3 に準拠した JVM で記述された長い文字列を読み込もうとすると、1.2 以前の JVM に java.io.StreamCorruptedException が返されます。

直列化のパフォーマンスの向上 (1.3 以降)
全般的なパフォーマンスを向上させるため、直列化にいくつかの変更が加えられました。
  • 不要なメモリ割り当ておよび同期/メソッド呼び出しのオーバーヘッドを減らすために、UTF 文字列の読み取り/書き込みが最適化されました。
  • プリミティブデータ配列の読み取りおよび書き込み用のコードが簡素化されました。ネイティブメソッドの呼び出し回数を最小にするため、float および double 型配列の読み取り/書き込みが再実装されました。
  • 内部バッファリングが改善されました。
  • 異なるネイティブメソッドの呼び出し回数を最小にするため、プリミティブフィールド値の取得/設定用リフレクション操作がバッチ化されました。

例外報告の改善 (1.3 以降)
直列化復元のクラス解決処理中にクラスが検出されなかった場合は、汎用的な例外ではなく、元の java.lang.ClassNotFoundException がスローされるようになりました。この結果、エラーについて詳細な情報を得られるようになりました。また、直列化復元の例外では、直列化復元中の上位クラスが報告されていましたが、検出されなかった実際のクラスの名前が保存されて報告されるようになりました。たとえば、RMI 呼び出しを行うと、スタブクラスは検出されるが、リモートインタフェースクラスが検出されないことがあります。この場合、現在の直列化機構では、検出されなかったクラスがそのインタフェースクラスであると正しく報告され、スタブクラスが検出されなかったという誤った報告は行われません。

java.io.ObjectOutputStream.writeClassDescriptor
java.io.ObjectInputStream.readClassDescriptor (1.3 以降)
java.io.ObjectStreamClass クラス記述子の直列化表現をカスタマイズする方法を提供するため、writeClassDescriptor メソッドと readClassDescriptor メソッドが追加されました。java.io.ObjectStreamClass のインスタンスの直列化が必要なときには、writeClassDescriptor が呼び出されます。また、writeClassDescriptorObjectStreamClass を直列化ストリームに書き込みます。逆に、直列化ストリーム内の次の項目として、ObjectInputStreamObjectStreamClass インスタンスを要求している場合は、readClassDescriptor が呼び出されます。ObjectOutputStream および ObjectInputStream のサブクラスは、これらのメソッドをオーバーライドすることにより、クラス記述子をアプリケーション固有の形式で送信できます。詳細は、「Java オブジェクト直列化仕様」の 2.1 および 3.1 節を参照してください。

java.io.ObjectOutputStream.annotateProxyClass
java.io.ObjectInputStream.resolveProxyClass (1.3 以降)
これらのメソッドは、目的の点で ObjectOutputStream.annotateClass および ObjectInputStream.resolveClass に類似しています。ただし、これらのメソッドは、非プロキシクラスとは対照的に動的プロキシクラス (java.lang.reflect.Proxy を参照) に適用される点が異なります。ObjectOutputStream のサブクラスは、annotateProxyClass をオーバーライドすることにより、カスタムデータを動的プロキシクラスの記述子とともにストリーム内に格納できます。ObjectInputStream サブクラスは、次に resolveProxyClass をオーバーライドすることにより、指定されたプロキシクラス記述子と関連付けるローカルクラスの選択にカスタムデータを利用します。詳細は、「Java オブジェクト直列化仕様」の 4 節を参照してください。

javadoc ツールタグ @serial@serialField、および @serialData (1.2 以降)
クラスの直列化形式をドキュメント化する手段を提供するために、javadoc タグ @serial@serialField および @serialData が、追加されました。javadoc は、これらのタグの内容を基にして直列化の仕様を生成します。詳細は、「Java オブジェクト直列化仕様」の 1.6 節を参照してください。

プロトコルのバージョン管理 (1.2 以降)
1.2 より前は、オブジェクト直列化で使用するプロトコルには、java.io.Externalizable インタフェースを実装するオブジェクトのクラスを使用できない場合に、そのオブジェクトをスキップする機能はありませんでした。1.2 では、この欠陥を解決する新たなプロトコルバージョンが追加されました。下位互換性を確保するため、ObjectOutputStream および ObjectInputStream は、新旧どちらのプロトコルで書き込まれた直列化ストリームに対しても、読み取りおよび書き込みが可能です。使用されるプロトコルバージョンは、ObjectOutputStream.useProtocolVersion メソッドを呼び出すことにより選択できます。互換性に関する問題の詳細は、「Java オブジェクト直列化仕様」の 6.3 節を参照してください。

クラス定義された writeReplace および readResolve メソッド (1.2 以降)
1.2 以降、クラスは、writeReplace および readResolve メソッドを定義できるようになりました。これらのメソッドを使用することにより、指定されたクラスのインスタンスは、直列化および直列化復元時にそのインスタンス自体の置換を指定できます。これらのメソッドに必須の署名、および詳細については、「Java オブジェクト直列化仕様」の 2.5 節および 3.6 節を参照してください。

java.io.ObjectOutputStream.writeObjectOverridejava.io.ObjectInputStream.readObjectOverride (1.2 以降)
1.2 以降、ObjectOutputStream および ObjectInputStream のサブクラスは、writeObjectOverride および readObjectOverride メソッドをオーバーライドすることにより、カスタム直列化プロトコルを実装できます。これらのメソッドが呼び出されるのは、ObjectOutputStream/ObjectInputStream サブクラスがアクセス権 java.io.SerializablePermission("enableSubclassImplementation") を保持し、ObjectOutputStream/ObjectInputStream の引数を持たないコンストラクタを呼び出す場合だけです。詳細は、「Java オブジェクト直列化仕様」の 2.1 節および 3.1 節を参照してください。

セキュリティアクセス権のチェック (1.2 以降)
ObjectOutputStream および ObjectInputStream のサブクラスは、継承したメソッドをオーバーライドすることにより、直列化プロセスの特定の局面への「フック」を取得できます。1.2 以降、オブジェクト直列化では、1.2 のセキュリティモデルを使用して、サブプロセスが特定のフックをオーバーライドするための適切なアクセス権を保持しているかどうかを確認しています。アクセス権 java.io.SerializablePermission("enableSubclassImplementation") および java.io.SerializablePermission("enableSubstitution") は、ObjectOutputStream.writeObjectOverride メソッド、ObjectOutputStream.replaceObject メソッド、ObjectInputStream.readObjectOverride メソッド、およびObjectInputStream.resolveObject メソッドが直列化の過程で呼び出されるかどうかを管理します。詳細は、「Java オブジェクト直列化仕様」の 2.1 節および 3.1 節を参照してください。

クラスの直列化可能フィールドの定義 (1.2 以降)
デフォルトでは、直列化可能クラスのインスタンスを直列化する際に、その直列化可能クラスのすべての非 static および非 transient フィールドの値が書き込まれます。1.2 では、クラスからこの処理をより細かく制御することのできる機構が導入されました。特別なフィールドである serialPersistentFields を宣言することにより、直列化可能クラスは、クラスまたはサブクラスのインスタンスの直列化時に書き込まれるフィールドを決定できます。この機能により、クラス内の実際のフィールドに直接関連しない直列化可能フィールドを、クラスから「定義」できるようにもなりました。この機能を次に説明する直列化可能フィールド API とともに使用することにより、クラスの直列化表現を変更せずに、クラスに対しフィールドを追加または削除できます。詳細は、「Java オブジェクト直列化仕様」の 1.5 節および 1.7 節を参照してください。

直列化可能フィールド API (1.2 以降)
1.2 で導入された直列化可能フィールド API を使用すると、クラス定義の writeObject/readObject メソッドから、直列化可能フィールドの値を、名前と型によって明示的に設定して取得できます。クラスに以前のクラスバージョンとの下位互換性が必要な場合は、この API は特に有用です。これは、クラスによっては、現在のクラスに直接マッピングできない一連の直列化可能フィールドを以前のバージョンで定義しているものがあるためです。この場合、新規バージョンのクラスでカスタムの writeObject および readObject メソッドを定義します。これらのメソッドは、(新規) クラスの指定されたインスタンスの内部状態を「以前の」直列化形式に変換できます。また、その逆も可能です。詳細は、「Java オブジェクト直列化仕様」の 1.7 節を参照してください。
* この Web サイトで使用されている用語「Java 仮想マシン」または「JVM」は、Java プラットフォーム用の仮想マシンを表します。
Copyright © 2004 Sun Microsystems, Inc.All Rights Reserved. 
コメントの送付先: rmi-comments@java.sun.com 
Sun