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

ここでは、Java™ SE Development Kit 6 (JDK) 以前に行われた直列化の拡張機能について説明します。現行リリースでの拡張機能については、「Java SE Development Kit 6 での直列化の変更と拡張」を参照してください。

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

これらの API は、含まれる配列オブジェクトをセキュアな方法でより効率的に読み取るために使用できます。『Java オブジェクト直列化仕様』のセクション A.6「非共有の直列化復元されたオブジェクトの保護」を参照してください。

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

同様に、J2SE 1.4.0 から、ObjectInputStream.readFields または ObjectInputStream.readUnshared をオーバーライドするサブクラスで 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 メソッドが追加されました。writeClassDescriptor は、java.io.ObjectStreamClass のインスタンスの直列化が必要なときに呼び出され、ObjectStreamClass を直列化ストリームに書き込みます。逆に、直列化ストリーム内の次の項目として、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 © 1993, 2013, Oracle and/or its affiliates. All rights reserved.