web-dev-qa-db-ja.com

Javaシリアル化-Java.io.InvalidClassExceptionローカルクラスに互換性がありません

Serializableを実装するパブリッククラスは、他の複数のクラスによって拡張されています。これらのサブクラスのみが以前にシリアル化されました-スーパークラスではありません。

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

それが重要かどうかはわかりませんが、プライベートとしてマークされていませんでしたが、デフォルトの保護がありました-パッケージ保護されていると言うことができます

static final long serialVersionUID = -7588980448693010399L;

ただし、スーパークラスもサブクラスもreadObjectまたはwriteObjectを実装していませんが、いずれのサブクラスにも明示的に定義されたserialVersionUIDがありませんでした。スーパークラスで定義されたもので十分だと思いました。

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

これで、以前にシリアル化されたオブジェクトを読み戻そうとすると、例外がスローされます。これに似たもの:

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

これは、サブクラスのいずれかを宣言しなかったために使用されたデフォルトのserialVersionUIDが、スーパークラスと1つのサブクラスの変更により変更されたために発生したと考えられます。

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

54
Dale

@ DanielChapman はserialVersionUIDの適切な説明を提供しますが、解決策はありません。解決策は次のとおりです。すべてのoldクラスで serialver プログラムを実行します。これらのserialVersionUID値をcurrentバージョンのクラスに入れます。現在のクラスが古いバージョンとシリアル互換性がある限り、問題ありません。 (将来のコードに注意してください:alwaysallserialVersionUIDクラスにSerializableを持つ必要があります)

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

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

45
jtahlborn

ここでの短い答えは、シリアルIDは、指定しない場合はハッシュを介して計算されるということです。 (静的メンバーは継承されません。静的メンバーは(1)のみであり、クラスに属します)。

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

GetSerialVersionUIDメソッドは、このクラスのserialVersionUIDを返します。 4.6項「ストリーム一意識別子」を参照してください。クラスで指定されていない場合、返される値は、National Institute of Standardsで定義されているSecure Hash Algorithm(SHA)を使用して、クラスの名前、インターフェイス、メソッド、およびフィールドから計算されたハッシュです。

クラスまたはその階層を変更すると、ハッシュが異なります。これは良いことです。オブジェクトは、メンバーが異なるため、異なるものになりました。そのため、シリアル化された形式から読み戻すと、実際には別のオブジェクトになります(つまり例外です)。

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

29

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

private static final long serialVersionUID = 1L;
18
taxeeta

これは私のために働いた:

Serializedクラスオブジェクトをファイルに書き込んでから、ファイルに変更を加えてコンパイルし、オブジェクトを読み取ろうとすると、これが発生します。

そのため、クラスが変更および再コンパイルされた場合、必要なオブジェクトをファイルに再度書き込みます。

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

6
skrtbhtngr