2011-12-01 30 views
40

私は、Serializableを実装するパブリッククラスを持っています。これは複数の他のクラスによって拡張されています。以前はスーパークラスではなく、それらのサブクラスしか以前にシリアル化されていませんでした。Javaのシリアル化 - java.io.InvalidClassExceptionローカルクラスの互換性がありません

スーパークラスはserialVersionUIDを定義していました。

私はそれが重要なのかどうかわからないんだけど、それはプライベートマークされていなかったのではなく、それだけでデフォルトの保護を持っていた - あなたはそれがパッケージだったと言うかもしれないが

static final long serialVersionUID = -7588980448693010399L; 

スーパークラス、またのいずれかを保護しましたただし、サブクラスはreadObjectまたはwriteObjectを実装しており、明示的に定義されたserialVersionUIDを持つサブクラスはありません。私は、スーパークラスで定義されたもので十分であると考えました。

これにもかかわらず、新しいインスタンス変数、List/ArrayList、新しいメソッドがスーパークラスに追加され、いくつかのプライベートインスタンス変数が追加されるまで、以前にシリアライズされたオブジェクトを読み返すまでは問題ありませんでしたそのサブクラスの1つ。

これまでにシリアル化されたオブジェクトを読み取ろうとすると、例外がスローされます。これと同様のもの:私はサブクラスのいずれか1つを宣言していないので、使用されたデフォルトのserialVersionUIDのは、今、変更されているので、私はこれを仮定してい

com.SomeCompany.SomeSubClass; local class incompatible: stream classdesc serialVersionUID = 1597316331807173261, local class serialVersionUID = -3344057582987646196 

が原因スーパークラスの変化に起因しています1つのサブクラス。

このジレンマから脱出する方法についてのご意見をいただければ幸いです。私はreadObjectとwriteObjectを実装する必要があると仮定していますが、defaultReadObject()とdefaultWriteObject()を呼び出す以外に、私は何をする必要があるのか​​正確にはわかりません。 serialVerisonUIDをすべてのサブクラスに追加する必要があるかどうか、または各サブクラスでreadObjectとwriteObjectを実装する必要があるかどうか、またはスーパークラスで一度だけ実装することができるかどうかはわかりません。

答えて

36

@DanielChapmanは、serialVersionUIDを説明しますが、解決策はありません。その解決方法は次のとおりです。すべてクラスのserialverプログラムを実行します。これらのserialVersionUID値を現在のバージョンのクラスに入れてください。現在のクラスが古いバージョンとシリアル互換である限り、うまくいくはずです。 (将来のコードのためのメモ:常にserialVersionUIDSerializableすべて上のクラスを持っている必要があります)

新しいバージョンがないシリアル互換性がある場合は、カスタムreadObject実装でいくつかの魔法を行う必要があります( 新しいクラスのデータを古いコードと互換性があるように書く場合は、カスタムwriteObjectが必要です。クラスフィールドを追加または削除しても、クラスシリアルは互換性がありません。既存のフィールドのタイプを変更することは通常行います。

もちろん、新しいクラスと互換性がある場合でも、カスタムのreadObject実装が必要な場合があります。古いバージョンのクラスから保存されたデータに欠けている新しいフィールドを入力したい場合(たとえば、古いクラスデータを読み込むときに空のリストに初期化する新しいリストフィールドがある場合)

+4

...古いクラスがない場合は、例外メッセージから古いserialVersionUIDを取得し、必要に応じて繰り返します。 –

+0

あなたの答えと[truthealitiy's](http://stackoverflow.com/a/10378907/712526)には、すべての関連する詳細が含まれています。たぶん質問が合併される可能性がありますか? – jpaugh

22

シリアルIDは、指定しないとハッシュで計算されます。 (静的メンバーは継承されません。静的メンバーは(1)しかなく、クラスに属します)。

http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html

getSerialVersionUID方法は、この クラスのserialVersionUIDを返します。 4.6項「ストリーム固有の識別子」を参照してください。クラスによって が指定されていない場合、返される値は、 クラスの名前、インターフェイス、メソッド、およびフィールドから計算されたハッシュであり、Secure Hash アルゴリズム(National Institute of Standards)で定義されています。

クラスまたはその階層を変更すると、ハッシュが異なることになります。これは良いことです。あなたのオブジェクトは異なったメンバーを持っているので異なっています。したがって、シリアル化されたフォームから読み込んだ場合、それは実際には別のオブジェクトになります。したがって例外です。

長い答えは、シリアル化は非常に便利ですが、それ以外の方法がない限り永続化には使用しないでください。特にあなたが経験していることのために、その危険な道。純粋なJavaプロジェクトの場合は、データベース、XML、ファイル形式、おそらくJPAなどの永続性構造を考慮する必要があります。

12

私にとっては、デフォルトのシリアルIDを追加するのを忘れていました。

private static final long serialVersionUID = 1L; 
+0

+1;原則として、Netbeansの新しいEntityクラスファイルを作成するときに自動的に追加されます。 – Omar

4

これは私の仕事:

ファイルにあなたの直列化されたクラスオブジェクトを書いた場合は、それを提出し、コンパイルするためにいくつかの変更を加え、その後、あなたは、オブジェクトを読み取るために、この現象が発生しますしてみてください。

したがって、クラスを変更して再コンパイルする場合は、必要なオブジェクトを再度ファイルに書き込んでください。

PS:これは解決策ではありません。回避策として意図されていました。

+0

これを行う必要はなく、既存のシリアル化をすべて失うこともありません。他の答えを見てください。 – EJP

+0

これは回避策ではなく、解決策であることを前提としていました。 – skrtbhtngr

関連する問題