オブジェクト直列化の拡張機能

次の項目について説明します。

Java SE 6での拡張機能

java.io.ObjectStreamClass.lookupAny
以前のリリースでは、直列化が不可能なClassObjectStreamClassインスタンスをプログラムで取得することは困難でした。しかし、クラス記述子のストリーム形式をカスタマイズするときは、そのようにすることが望ましい場合もあります(詳細は、4413615を参照)。新しいメソッドObjectStreamClass.lookupAnyをこのために使用できるようになりました。
バグの修正: 遅延ガベージ・コレクション
以前のリリースでは、バグ6232010により、ObjectOutputStreamおよびObjectInputStreamの直列化可能クラスおよびサブクラスを直列化操作で使用後しばらくたっても、これらのクラスへの強い参照がある場合がありました。このためこれらを定義するクラス・ローダーのガベージ・コレクションが無期限に遅れる可能性がありました。このバグを修正するために、直列化の実装で内部キャッシュが再構築されました。

Java SE 5.0での拡張機能

列挙型インスタンスの直列化のサポート
列挙型の処理のサポートが直列化に追加されました。これは、このリリースでの新機能です。enumインスタンスを直列化するルールは、「通常の」直列化可能オブジェクトを直列化するルールとは異なります。直列化された形式のenumインスタンスは、そのenum定数名と、その基になっているenum型を識別する情報のみで構成されています。同様に、直列化の復元動作も異なります。クラス情報は適切なenumクラスを見つけるために使用されます。enum定数を取得して返すために、そのクラスと受け取った定数名を使用してEnum.valueOfメソッドが呼び出されます。
バグの修正: java.lang.ClassNotFoundExceptionのためjava.io.StreamCorruptedExceptionがスローされる
1.4.0以降の以前のリリースでは、ObjectInputStream.readClassDescriptorメソッドによってスローされるClassNotFoundExceptionは、StreamCorruptedExceptionとしてObjectInputStream.readObjectのトップレベルの呼出し側に反映され、その原因は空であることだとされました。現在は、InvalidClassExceptionとしてトップ・レベルの呼出し側に反映されるようになり、その原因は元のClassNotFoundExceptionとなります。
バグの修正: [sic]からの通知をjava.io.ObjectStreamClass$EntryFutureで待機するスレッド
1.4.0以降の以前のリリースでは、ObjectStreamClass.lookupメソッドのClass引数で表されるクラスの静的初期化子内で、このメソッドが呼び出された場合にデッドロックする可能性がありました。この場合、デッドロックが発生することはなくなりました。
バグの修正: serialVersionUIDの仕様なし
Serializableインタフェースのjavadocが拡張され、serialVersionUIDの役割と使用方法をさらに詳細に指定できるようになり、また、直列化可能なクラスのserialVersionUIDを明示的に指定する必要性が明確となりました。

Java SE 1.4での拡張機能

非共有オブジェクトの直列化復元のサポート
データ直列化ストリームで非共有として認識されるオブジェクトの直列化復元も、直列化でサポートするようになりました。この新しいサポートは、パッケージjava.ioに次のようなAPIを追加することにより提供されます。

これらのAPIは、含まれる配列オブジェクトをセキュアな方法でより効率的に読み取るために使用できます。

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

ObjectInputStream.readFieldsまたはObjectInputStream.readUnsharedをオーバーライドするサブクラスでObjectInputStreamの引数を1つ取るpublicのコンストラクタを直接的または間接的に呼び出すとき、"enableSubclassImplementation" SerializablePermissionが必要になりました。

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

クラス定義メソッドreadObjectNoDataのサポート
クラス定義メソッドの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オブジェクトの直列化復元エラー
以前のリリースでは、プリミティブ型のClassオブジェクトの直列化復元を試行するとClassNotFoundExceptionエラーになりました(バグ4171142)。これは、プリミティブ型のObjectStreamClass記述子にはObjectInputStream.resolveClass()を使用できないことが問題でした。このリリースではこのバグは修正されました。
バグの修正: public以外のインタフェースでObjectInputStream.resolveProxyClassがエラーになることがある
以前のリリースでは、1つ以上のプロキシ・インタフェースがpublicでない場合、ObjectInputStream.resolveProxyClassはプロキシ・クラスを定義するクラス・ローダーを正しく選択するとはかぎりませんでした。このリリースでは、ObjectInputStream.resolveProxyClassがpublic以外のインタフェースを検出すると、実装するプロキシ・クラスをインタフェースと同じクラス・ローダーに定義しようとし、競合する場合は例外をスローします。これは、プロキシがインタフェースを実装するために必要です。
バグの修正: 無効なserialPersistentFieldsのフィールド名によるNullPointerExceptionの発生
以前のリリースでは、デフォルトの直列化を使用するが実際のクラス・フィールドにマップされていないserialPersistentFieldエントリを宣言するオブジェクトを直列化すると、NullPointerExceptionsがスローされました(バグ4387368)。このリリースでは、そのような場合、直列化はInvalidClassExceptionsをスローします。デフォルトの直列化を使用するのであれば、そのような「サポートされない」serialPersistentFieldsを定義する必要はないからです。
バグの修正: スキップされたオブジェクトでのClassNotFoundExceptionによる直列化エラー
以前のリリースでは、「スキップされた」オブジェクト(直列化復元パーティによってロードされたクラスにないフィールドに関連付けられたオブジェクト)によりClassNotFoundExceptionsが発生すると、オブジェクト・グラフ全体の直列化復元エラーとなりました。これは、スキップされた値がグラフに含まれていない場合も同様でした。今回のリリースの直列化では、このようなスキップされたオブジェクトに関連付けられたClassNotFoundExceptionsを無視し、不要な直列化復元エラーのクラスを排除することでこの問題に対処しています。直列化復元中に発生するClassNotFoundExceptionsに関連して、直列化全体を堅牢にするための変更がこの他にも行われています。

Java SE 1.3での拡張機能

64Kを超える文字列を直列化できるようになる
このリリースより前は、64Kを超える文字列を直列化しようとすると、java.io.UTFDataFormatExceptionがスローされました。このリリースでは、64Kを超える文字列を直列化できるように、直列化プロトコルが拡張されました。ただし、1.2以前のJVMで、1.3に準拠したJVMで記述された長い文字列を読み込もうとすると、1.2以前のJVMはjava.io.StreamCorruptedExceptionを受け取ります。
直列化のパフォーマンスの向上
全般的なパフォーマンスを向上させるため、直列化にいくつかの変更が加えられました。
  • 不要なメモリー割当ておよび同期/メソッド呼出しのオーバーヘッドを減らすために、UTF文字列の読み取り/書込みが最適化されました。
  • プリミティブ・データ配列の読み取りおよび書込み用のコードが簡素化されました。ネイティブ・メソッドの呼出し回数を最小にするため、floatおよびdouble型配列の読み取り/書込みが再実装されました。
  • 内部バッファリングが改善されました。
  • 異なるネイティブ・メソッドの呼出し回数を最小にするため、プリミティブ・フィールド値の取得/設定用リフレクション操作がバッチ化されました。
例外報告の改善
直列化復元のクラス解決処理中にクラスが検出されなかった場合は、汎用的な例外ではなく、元のjava.lang.ClassNotFoundExceptionがスローされるようになりました。この結果、エラーについて詳細な情報を得られるようになりました。また、直列化復元の例外では、直列化復元中の上位クラスが報告される代わりに、検出されなかった元のクラスの名前が保存されて報告されるようになりました。たとえば、RMI呼出しを行うと、スタブ・クラスは検出されるが、リモート・インタフェース・クラスが検出されないことがあります。この場合、現在の直列化メカニズムでは、検出されなかったクラスがそのインタフェース・クラスであると正しく報告され、スタブ・クラスが検出されなかったという誤った報告は行われません。
java.io.ObjectOutputStream.writeClassDescriptor
java.io.ObjectInputStream.readClassDescriptor
java.io.ObjectStreamClassクラス記述子の直列化表現をカスタマイズする方法を提供するために、writeClassDescriptorメソッドとreadClassDescriptorメソッドが追加されました。writeClassDescriptorは、java.io.ObjectStreamClassのインスタンスの直列化が必要なときに呼び出され、ObjectStreamClassを直列化ストリームに書き込む役割を果たします。逆に、直列化ストリーム内の次の項目として、ObjectInputStreamObjectStreamClassインスタンスを要求しているときは、readClassDescriptorが呼び出されます。ObjectOutputStreamおよびObjectInputStreamのサブクラスは、これらのメソッドをオーバーライドすることにより、クラス記述子をアプリケーション固有の形式で送信できます。詳細は、『Javaオブジェクト直列化仕様』のセクション2.1および3.1を参照してください。
java.io.ObjectOutputStream.annotateProxyClass
java.io.ObjectInputStream.resolveProxyClass
これらのメソッドは、その目的がObjectOutputStream.annotateClassおよびObjectInputStream.resolveClassと似ています。ただし、これらのメソッドは、非プロキシ・クラスとは対照的に動的プロキシ・クラス(java.lang.reflect.Proxyを参照)に適用される点が異なります。ObjectOutputStreamのサブクラスは、annotateProxyClassをオーバーライドすることにより、カスタム・データを動的プロキシ・クラスの記述子とともにストリーム内に格納できます。ObjectInputStreamサブクラスは、次にresolveProxyClassをオーバーライドすることにより、指定されたプロキシ・クラス記述子と関連付けるローカル・クラスの選択にカスタム・データを利用します。詳細は、『Javaオブジェクト直列化仕様』のセクション4を参照してください。
javadocツール・タグ@serial@serialField、および@serialData
クラスの直列化形式をドキュメント化する手段を提供するために、javadocタグ@serial@serialField、および@serialDataが追加されました。javadocは、これらのタグの内容を基にして直列化の仕様を生成します。詳細は、『Javaオブジェクト直列化仕様』のセクション1.6を参照してください。

Java SE 1.2での拡張機能

プロトコルのバージョン管理
このリリースより前は、オブジェクト直列化で使用するプロトコルには、java.io.Externalizableインタフェースを実装するオブジェクトのクラスを使用できない場合に、そのオブジェクトをスキップする機能はありませんでした。このリリースでは、この欠陥を解決する新たなプロトコル・バージョンが追加されました。下位互換性を確保するため、ObjectOutputStreamおよびObjectInputStreamは、どちらのプロトコルで書き込まれた直列化ストリームに対しても、読取りおよび書込みが可能です。使用されるプロトコル・バージョンは、ObjectOutputStream.useProtocolVersionメソッドを呼び出すことにより選択できます。互換性に関する問題の詳細は、『Javaオブジェクト直列化仕様』のセクション6.3を参照してください。
クラス定義されたwriteReplaceおよびreadResolveメソッド
クラスで、writeReplaceおよびreadResolveメソッドを定義できます。これらのメソッドを使用することにより、指定されたクラスのインスタンスは、直列化および直列化復元時にそのインスタンス自体の置換を指定できます。これらのメソッドに必須のシグネチャ、および詳細については、『Javaオブジェクト直列化仕様』のセクション2.5および3.6を参照してください。
java.io.ObjectOutputStream.writeObjectOverridejava.io.ObjectInputStream.readObjectOverride
ObjectOutputStreamおよびObjectInputStreamのサブクラスで、writeObjectOverrideおよびreadObjectOverrideメソッドをオーバーライドすることにより、カスタム直列化プロトコルを実装できます。これらのメソッドが呼び出されるのは、ObjectOutputStream/ObjectInputStreamサブクラスがアクセス権java.io.SerializablePermission("enableSubclassImplementation")を保持し、ObjectOutputStream/ObjectInputStreamの引数を持たないコンストラクタを呼び出す場合だけです。詳細は、『Javaオブジェクト直列化仕様』のセクション2.1および3.1を参照してください。
セキュリティ・アクセス権のチェック
ObjectOutputStreamおよびObjectInputStreamのサブクラスは、継承したメソッドをオーバーライドすることにより、直列化プロセスの特定の局面への「フック」を取得できます。このリリース以降、オブジェクト直列化では、1.2のセキュリティ・モデルを使用して、サブクラスが特定のフックをオーバーライドするための適切なアクセス権を保持しているかどうかを確認しています。アクセス権java.io.SerializablePermission("enableSubclassImplementation")およびjava.io.SerializablePermission("enableSubstitution")は、ObjectOutputStream.writeObjectOverrideメソッド、ObjectOutputStream.replaceObjectメソッド、ObjectInputStream.readObjectOverrideメソッド、およびObjectInputStream.resolveObjectメソッドが直列化の過程で呼び出されるかどうかを管理します。詳細は、『Javaオブジェクト直列化仕様』のセクション2.1および3.1を参照してください。
クラスの直列化可能フィールドの定義
デフォルトでは、直列化可能クラスのインスタンスを直列化する際に、その直列化可能クラスのすべての非staticおよび非transientフィールドの値が書き込まれます。このリリースでは、クラスからこの処理をより細かく制御することのできる新しいメカニズムが導入されました。特別なフィールドであるserialPersistentFieldsを宣言することにより、直列化可能クラスは、クラスまたはサブクラスのインスタンスの直列化時に書き込まれるフィールドを決定できます。この機能により、クラス内の実際のフィールドに直接関連しない直列化可能フィールドを、クラスから「定義」できるようにもなりました。この機能を次に説明する直列化可能フィールドAPIとともに使用することにより、クラスの直列化表現を変更せずに、クラスに対しフィールドを追加または削除できます。詳細は、『Javaオブジェクト直列化仕様』のセクション1.5および1.7を参照してください。
直列化可能フィールドAPI
このリリースで導入された直列化可能フィールドAPIを使用すると、クラス定義のwriteObjectまたはreadObjectメソッドから、直列化可能フィールドの値を、名前と型によって明示的に設定して取得できます。クラスに以前のクラス・バージョンとの下位互換性が必要な場合は、このAPIは特に有用です。これは、クラスによっては、現在のクラスのフィールドに直接マッピングできない一連の直列化可能フィールドを以前のバージョンで定義しているものがあるためです。この場合、新規バージョンのクラスでカスタムのwriteObjectおよびreadObjectメソッドを定義します。これらのメソッドは、(新規)クラスの指定されたインスタンスの内部状態を「以前の」直列化形式に変換できます。また、その逆も可能です。詳細は、『Javaオブジェクト直列化仕様』のセクション1.7を参照してください。

Copyright © 1993, 2020, Oracle and/or its affiliates. All rights reserved.