serialVersionUID
がないと、Eclipseは警告を出します。
直列化可能クラスFooは、long型のstatic final serialVersionUIDフィールドを宣言しません。
serialVersionUID
とは何ですか。なぜそれが重要なのですか? serialVersionUID
がないと問題が発生する例を示してください。
Java.io.Serializable
のドキュメントは、おそらくあなたが得るのと同じくらい良い説明です:
シリアル化ランタイムは、各シリアル化可能クラスに
serialVersionUID
と呼ばれるバージョン番号を関連付けます。これは、シリアル化されたオブジェクトの送信者と受信者がシリアル化に関して互換性のあるオブジェクトのクラスをロードしたことを確認するために、逆シリアル化中に使用されます。受信側が、対応する送信側のクラスとは異なるserialVersionUID
を持つオブジェクトのクラスをロードした場合、逆シリアル化はInvalidClassException
になります。直列化可能クラスは、静的で、finalで、タイプがserialVersionUID
でなければならないserialVersionUID
という名前のフィールドを宣言することにより、独自のlong
を明示的に宣言できます。ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
直列化可能クラスが
serialVersionUID
を明示的に宣言しない場合、直列化ランタイムは、Java(TM)Object Serialization Specificationで説明されているように、クラスのさまざまな側面に基づいてそのクラスのデフォルトのserialVersionUID
値を計算します。ただし、デフォルトのserialVersionUID
計算は、コンパイラーの実装に応じて異なる可能性があるクラスの詳細に非常に敏感であり、したがって、逆シリアル化中に予期しないserialVersionUID
値になるため、すべてのシリアライズ可能なクラスが明示的にInvalidClassExceptions
値を宣言することは強く推奨です。したがって、異なるJavaコンパイラー実装で一貫したserialVersionUID
値を保証するには、シリアライズ可能なクラスで明示的なserialVersionUID
値を宣言する必要があります。また、明示的なserialVersionUID
宣言は可能な限りprivate修飾子を使用することを強くお勧めします。そのような宣言は、すぐに宣言するクラスserialVersionUID
フィールドにのみ適用され、継承メンバーとしては役に立たないためです。
実装のためにシリアル化する必要があるという理由だけでシリアル化する場合(たとえば、HTTPSession
をシリアル化するかどうかを気にします...格納されているかどうかに関係なく、おそらくde-serializing
フォームオブジェクト)、これは無視できます。
実際にシリアル化を使用している場合は、シリアル化を直接使用してオブジェクトを保存および取得する予定がある場合にのみ問題になります。 serialVersionUID
はクラスのバージョンを表します。クラスの現在のバージョンに以前のバージョンとの下位互換性がない場合は、それをインクリメントする必要があります。
ほとんどの場合、シリアル化を直接使用することはおそらくないでしょう。この場合、クイック修正オプションをクリックしてデフォルトのSerialVersionUID
を生成しますが、心配する必要はありません。
Josh Blochの著書 Effective Java (2nd Edition)を差し込むことはできません。第11章は、Javaシリアライゼーションに欠かせない資料です。
Joshによると、自動生成されたUIDはクラス名、実装されたインターフェース、そしてすべてのパブリックおよび保護メンバーに基づいて生成されます。これらのいずれかを何らかの方法で変更すると、serialVersionUID
が変更されます。ですから、(プロセスをまたがって、または後でストレージから取得されるようにして)クラスの1つのバージョンしかシリアライズされないことが確実な場合にのみ、それらを混乱させる必要はありません。
今のところそれらを無視し、後でクラスを何らかの方法で変更する必要があるが古いバージョンのクラスとの互換性を維持する必要があることがわかった場合は、JDKツール serialver を使用してserialVersionUID
を生成できます。 old classを明示的に設定し、それを新しいクラスに明示的に設定します。 (変更内容によっては、writeObject
およびreadObject
メソッドを追加してカスタムシリアル化も実装する必要があるかもしれません。javadocのSerializable
または前述の第11章を参照してください。)
これらのserialVersionUID警告を無視するようにEclipseに指示することができます。
ウィンドウ>設定> Java>コンパイラ>エラー/警告>潜在的なプログラミングの問題
知らない場合は、このセクションで有効にできる他の多くの警告(またはエラーとして報告されているものもあります)があり、その多くは非常に便利です。
などなど。
serialVersionUID
はシリアル化されたデータのバージョン管理を容易にします。その値は、シリアル化時にデータとともに格納されます。デシリアライズするとき、シリアライズされたデータが現在のコードとどのように一致するかを確認するために同じバージョンがチェックされます。
データをバージョン管理したい場合は、通常、serialVersionUID
を0にして、シリアル化されたデータを変更するクラスへの構造的な変更(非一時フィールドの追加または削除)を行うたびにそれを増やします。
組み込みのシリアル化解除メカニズム(in.defaultReadObject()
)は、古いバージョンのデータからのシリアル化解除を拒否します。しかし、あなたが望むなら、古いデータを読み返すことができるあなた自身の readObject() - 関数を定義することができます。このカスタムコードは、データがどのバージョンにあるのかを知るためにserialVersionUID
をチェックし、それをデシリアライズする方法を決定することができます。このバージョン管理手法は、コードのいくつかのバージョンに耐えるシリアル化されたデータを格納する場合に役立ちます。
しかし、そのような長い期間にわたってシリアル化されたデータを保存することはあまり一般的ではありません。シリアル化メカニズムを使用して、例えばキャッシュに一時的にデータを書き込むか、コードベースの関連部分が同じバージョンの別のプログラムにネットワーク経由で送信するのがはるかに一般的です。
この場合、後方互換性を維持することには興味がありません。あなたは、通信しているコードベースが実際に同じバージョンの関連クラスを持っていることを確認することだけを心配しています。そのようなチェックを容易にするために、serialVersionUID
を以前と同じように維持し、クラスを変更するときにそれを更新することを忘れないでください。
フィールドを更新することを忘れた場合、異なる構造を持つが同じserialVersionUID
を持つ2つの異なるバージョンのクラスになる可能性があります。この場合、デフォルトのメカニズム(in.defaultReadObject()
)は違いを検出せず、互換性のないデータをデシリアライズしようとします。これで、暗号化されたランタイムエラーまたはサイレントエラー(nullフィールド)が発生する可能性があります。この種のエラーは見つけにくいかもしれません。
そのため、このユースケースを支援するために、JavaプラットフォームではserialVersionUID
を手動で設定しないことを選択できます。代わりに、クラス構造のハッシュがコンパイル時に生成され、idとして使用されます。このメカニズムによって、同じIDを持つ異なるクラス構造を持つことが確実になくなるため、上記のようなトレースが困難な実行時シリアル化の失敗を防ぐことができます。
しかし、自動生成されたID戦略には裏側があります。つまり、同じクラスに対して生成されたIDがコンパイラによって異なる可能性があるということです(上記のJon Skeetが述べています)。そのため、異なるコンパイラでコンパイルされたコード間でシリアル化されたデータをやり取りする場合は、とにかく手動でIDを維持することをお勧めします。
また、最初に述べたユースケースのように、データと後方互換性がある場合は、おそらくIDを自分で管理したいと思うでしょう。これは、読みやすいIDを取得し、いつどのように変更されるのかを制御するためです。
serialVersionUID とは何ですか。なぜ使用する必要がありますか。
SerialVersionUID
は各クラスの一意の識別子です。JVM
はそれを使用してクラスのバージョンを比較し、直列化中に同じクラスが使用されたことを確認して、直列化復元中にロードします。
1を指定するとより細かく制御できますが、指定しない場合はJVMによって制御されます。生成される値はコンパイラによって異なります。さらに、時々あなたは古いシリアライズされたオブジェクト[backward incompatibility
]のデシリアライゼーションを禁止するために何らかの理由で欲しいだけです、そしてこの場合あなたはちょうどserialVersionUIDを変更しなければなりません。
Serializable
の javadocs say :
デフォルトのserialVersionUIDの計算は、コンパイラの実装によって異なる可能性があるクラスの詳細に非常に敏感であり、逆シリアル化中に予期せぬ
InvalidClassException
sが発生する可能性があります。
それゆえ、あなたはそれが私たちにもっと制御を与えるのでserialVersionUIDを宣言しなければなりません .
この記事 はこのトピックに関していくつかの良い点を持っています。
元の質問では、「なぜそれが重要なのか」と「例」が求められていましたが、このSerial Version ID
が有用な場合があります。まあ、私はそれを見つけました。
Car
クラスを作成し、それをインスタンス化し、それをオブジェクトストリームに書き出すとしましょう。平坦化されたcarオブジェクトはしばらくの間ファイルシステムに置かれています。一方、Car
クラスが新しいフィールドを追加することによって変更された場合。後で、フラット化されたCar
オブジェクトを読み取ろうとすると(つまり逆シリアル化しようとすると)、Java.io.InvalidClassException
を取得します。これは、すべての直列化可能クラスに自動的に一意の識別子が与えられるためです。この例外は、クラスの識別子が平坦化されたオブジェクトの識別子と等しくない場合にスローされます。あなたが本当にそれについて考えるならば、例外は新しい分野の追加のために投げられます。明示的なserialVersionUIDを宣言することによってバージョン管理を自分で制御することで、この例外がスローされるのを避けることができます。あなたのserialVersionUID
を明示的に宣言することには小さなパフォーマンス上の利点もあります(なぜなら計算する必要はないからです)。そのため、次に示すように作成したらすぐに、独自のserialVersionUIDをSerializableクラスに追加することをお勧めします。
public class Car {
static final long serialVersionUID = 1L; //assign a long value
}
オブジェクトをバイト配列にシリアル化して送信/保存する必要がまったくない場合は、心配する必要はありません。もしそうであれば、オブジェクトのデシリアライザがそれをそのクラスローダが持っているオブジェクトのバージョンにマッチさせるので、あなたはserialVersionUIDを考慮しなければなりません。詳しくはJava言語仕様を読んでください。
この警告がクラスに対して発生した場合は、シリアル化については考えていません。自分自身をimplements Serializable
と宣言していないのは、多くの場合、Serializableを実装するスーパークラスから継承したためです。多くの場合、継承を使用するのではなく、そのようなオブジェクトに委任することをお勧めします。
だから、の代わりに
public class MyExample extends ArrayList<String> {
public MyExample() {
super();
}
...
}
行う
public class MyExample {
private List<String> myList;
public MyExample() {
this.myList = new ArrayList<String>();
}
...
}
そして関連するメソッドでmyList.foo()
(またはthis.foo()
)の代わりにsuper.foo()
を呼び出します。 (これはすべての場合に当てはまるわけではありませんが、それでもかなり頻繁に起こります。)
私は、JFrameなどを拡張する必要があるときによく見かけます。 (JFrameには何百ものメソッドがあるので、これはIDEでの自動補完にも役立ちます。クラスでカスタムのメソッドを呼び出す場合は不要です。)
警告(またはserialVersionUID)が避けられない場合の1つのケースは、通常は無名クラスのAbstractActionから拡張し、actionPerformed-methodを追加することだけです。このような場合には警告は表示されないと思います(通常、クラスのバージョンが異なる場合、このような無名クラスを信頼性のある方法で直列化および逆直列化することはできません)。
まず、シリアル化について説明する必要があります。
シリアル化ネットワーク経由でオブジェクトを送信するためにオブジェクトをストリームに変換できますORファイルに保存ORレター用にDBに保存使用法。
シリアル化にはいくつかのルールがあります。
オブジェクトは、そのクラスまたはスーパークラスがSerializableインターフェイスを実装している場合にのみシリアル化可能です。
オブジェクトは、スーパークラスがそうでない場合でも、シリアル化可能です(それ自体がSerializableインターフェースを実装します)。ただし、Serializableインターフェイスを実装しない、直列化可能クラスの階層内の最初のスーパークラスには、引数なしのコンストラクタが必要です。これに違反すると、readObject()は実行時にJava.io.InvalidClassExceptionを生成します
すべてのプリミティブ型はシリアル化可能です。
一時フィールド(一時修飾子付き)はシリアル化されません(つまり、保存も復元もされません)。 Serializableを実装するクラスは、シリアル化をサポートしないクラス(ファイルストリームなど)の一時フィールドをマークする必要があります。
静的フィールド(静的修飾子付き)はシリアル化されません。
オブジェクトがシリアル化されるときJava Runtime serialVersionIDと呼ばれるシリアルバージョン番号を関連付けます。
serialVersionIDが必要な場所:逆シリアル化中に、送信者と受信者がシリアル化に関して互換性があることを確認します。異なるserialVersionIDを持つクラスは、逆シリアル化がInvalidClassCastExceptionで終了します。
シリアル化可能なクラスは、「serialVersionUID」という名前のフィールドを宣言することにより、独自のserialVersionUIDを明示的に宣言できます。
例でこれを試してみましょう。
import Java.io.Serializable;
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String empname;
private byte empage;
public String getEmpName() {
return name;
}
public void setEmpName(String empname) {
this.empname = empname;
}
public byte getEmpAge() {
return empage;
}
public void setEmpAge(byte empage) {
this.empage = empage;
}
public String whoIsThis() {
StringBuffer employee = new StringBuffer();
employee.append(getEmpName()).append(" is ).append(getEmpAge()).append("
years old "));
return employee.toString();
}
}
シリアル化オブジェクトを作成
import Java.io.FileOutputStream;
import Java.io.IOException;
import Java.io.ObjectOutputStream;
public class Writer {
public static void main(String[] args) throws IOException {
Employee employee = new Employee();
employee.setEmpName("Jagdish");
employee.setEmpAge((byte) 30);
FileOutputStream fout = new
FileOutputStream("/users/Jagdish.vala/employee.obj");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(employee);
oos.close();
System.out.println("Process complete");
}
}
オブジェクトのデシリアライズ
import Java.io.FileInputStream;
import Java.io.IOException;
import Java.io.ObjectInputStream;
public class Reader {
public static void main(String[] args) throws ClassNotFoundException,
IOException {
Employee employee = new Employee();
FileInputStream fin = new
FileInputStream("/users/Jagdish.vala/employee.obj");
ObjectInputStream ois = new ObjectInputStream(fin);
employee = (Employee) ois.readObject();
ois.close();
System.out.println(employee.whoIsThis());
}
}
注:ここで、EmployeeクラスのserialVersionUIDを変更して保存します。
private static final long serialVersionUID = 4L;
Readerクラスを実行します。 Writerクラスを実行しないと、例外が発生します。
Exception in thread "main" Java.io.InvalidClassException:
com.jagdish.vala.Java.serialVersion.Employee; local class incompatible:
stream classdesc serialVersionUID = 1, local class serialVersionUID = 4
at Java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.Java:616)
at Java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.Java:1623)
at Java.io.ObjectInputStream.readClassDesc(ObjectInputStream.Java:1518)
at Java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.Java:1774)
at Java.io.ObjectInputStream.readObject0(ObjectInputStream.Java:1351)
at Java.io.ObjectInputStream.readObject(ObjectInputStream.Java:371)
at com.krishantha.sample.Java.serialVersion.Reader.main(Reader.Java:14)
フィールドserialVersionUIDの意味を理解するためには、シリアライゼーション/デシリアライゼーションがどのように機能するかを理解する必要があります。
直列化可能クラスオブジェクトが直列化されると、Java Runtimeは直列バージョン番号(serialVersionUIDと呼ばれる)をこの直列化されたオブジェクトに関連付けます。この直列化オブジェクトを非直列化した時点で、Java Runtimeは直列化オブジェクトのserialVersionUIDをクラスのserialVersionUIDと一致させます。両方が等しい場合は、それだけがそれ以降の逆シリアル化プロセスを続行し、それ以外の場合はInvalidClassExceptionをスローします。
そのため、シリアライゼーション/デシリアライゼーションプロセスを成功させるには、シリアライズされたオブジェクトのserialVersionUIDがクラスのserialVersionUIDと同等である必要があると結論付けます。プログラマがプログラムで明示的にserialVersionUID値を指定した場合、シリアライゼーションおよびデシリアライゼーションのプラットフォームに関係なく、同じ値がシリアライズされたオブジェクトとクラスに関連付けられます(たとえば、SunやWindowsを使用してwindowsのようなプラットフォームで行われる場合があります)。 MS JVMとデシリアライゼーションは、Zing JVMを使用する異なるプラットフォームのLinux上で行われる可能性があります。
ただし、serialVersionUIDがプログラマによって指定されていない場合は、任意のオブジェクトのSerialization\DeSerializationを実行している間、Javaランタイムは独自のアルゴリズムを使用してそれを計算します。このserialVersionUID計算アルゴリズムはJREによって異なります。オブジェクトがシリアライズされる環境が1つのJRE(例:Sun JVM)を使用していて、逆シリアル化が行われる環境がLinux Jvm(zing)を使用している可能性もあります。そのような場合、直列化されたオブジェクトに関連付けられたserialVersionUIDは、直列化復元環境で計算されたクラスのserialVersionUIDとは異なります。逆シリアル化は成功しません。そのため、このような状況や問題を回避するために、プログラマは常にSerializableクラスのserialVersionUIDを指定する必要があります。
不足しているserialVersionUIDが問題を引き起こすかもしれない例に関しては:
私はEJB
モジュールを使用するWebモジュールで構成されているこのJava EEアプリケーションに取り組んでいます。 WebモジュールはEJB
モジュールをリモートで呼び出し、POJO
を引数として実装するSerializable
を渡します。
このPOJO's
クラスは、EJB jar内およびWebモジュールのWEB-INF/lib内の独自のjar内にパッケージ化されていました。これらは実際には同じクラスですが、EJBモジュールをパッケージ化するときに、このPOJOのjarを展開してEJBモジュールと一緒にパックします。
EJB
の呼び出しは、私がそのserialVersionUID
を宣言していなかったため、以下の例外で失敗しました。
Caused by: Java.io.IOException: Mismatched serialization UIDs : Source
(Rep.
IDRMI:com.hordine.pedra.softbudget.domain.Budget:5CF7CE11E6810A36:04A3FEBED5DA4588)
= 04A3FEBED5DA4588 whereas Target (Rep. ID RMI:com.hordine.pedra.softbudget.domain.Budget:7AF5ED7A7CFDFF31:6227F23FA74A9A52)
= 6227F23FA74A9A52
気にしないで、デフォルトの計算は本当に良く、99,9999%の場合に十分です。そして、あなたが問題に遭遇した場合、あなたは - すでに述べたように - UIDを導入する必要があります。
私は一般的にserialVersionUID
を1つのコンテキストで使用します。それがわかっているときは、それはJava VMのコンテキストを離れることになるでしょう。
私は自分のアプリケーションにObjectInputStream
とObjectOutputStream
を使うとき、または私が使うライブラリ/フレームワークを知っているときにはこれを知っています。 serialVersionIDは、さまざまなバージョンまたはベンダーの異なるJava VMが正しく相互動作すること、またはVM以外に格納および取得される場合(たとえばHttpSession
)にセッションデータがアプリケーションサーバーの再起動およびアップグレード中でも残ることを保証します。 。
他のすべての場合には、私は使用します
@SuppressWarnings("serial")
ほとんどの場合、デフォルトのserialVersionUID
で十分です。これにはException
、HttpServlet
が含まれます。
フィールドデータはクラスに格納されている情報を表します。クラスはSerializable
インターフェースを実装し、 Eclipseは自動的にserialVersionUID
フィールドを宣言することを提案しました。そこに設定された値1から始めましょう。
その警告を表示したくない場合は、次のようにします。
@SuppressWarnings("serial")
SerialVersionUIDはオブジェクトのバージョン管理に使用されます。クラスファイルでserialVersionUIDを指定することもできます。 serialVersionUIDを指定しないと、クラス内のフィールドを追加または変更したときに、新しいクラスと古いシリアル化オブジェクトに対して生成されたserialVersionUIDが異なるため、既にシリアル化されたクラスは回復できません。 Javaシリアライゼーションプロセスは、シリアライズされたオブジェクトの状態を回復するために正しいserialVersionUIDに依存し、serialVersionUIDが一致しない場合はJava.io.InvalidClassExceptionをスローします。
続きを読む: http://javarevisited.blogspot.com/2011/04/top-10-Java-serialization-interview.html#ixzz3VQxnpOPZ
CheckStyleが、Serializableを実装するクラスのserialVersionUIDに適切な値があること、つまりシリアルバージョンIDジェネレータが生成するものと一致することを確認できればいいでしょう。たとえば、シリアル化可能なDTOが多数あるプロジェクトがある場合、既存のserialVersionUIDを削除して再生成することは面倒です。現在のところ、これを確認する唯一の方法は(クラスごとに再生成して比較することです)古いもの。これはとても痛いです。
JavaのSerialVersionUID
クラス内でSerializable
を使用する理由
serialization
の間、Javaランタイムはクラスのバージョン番号を作成するので、後でそれを逆シリアル化することができます。このバージョン番号は、JavaではSerialVersionUID
として知られています。
SerialVersionUID
はシリアル化されたデータをバージョン管理するために使用されます。 SerialVersionUID
がシリアライズされたインスタンスと一致する場合にのみ、クラスをデシリアライズできます。クラスでSerialVersionUID
を宣言しないと、Javaランタイムによって生成されますが、お勧めできません。デフォルトのメカニズムを避けるためにSerialVersionUID
をprivate static final long
変数として宣言することをお勧めします。
マーカーインタフェースJava.io.Serializable
を実装してクラスをSerializable
として宣言すると、Externalizable
インタフェースを使用してプロセスをカスタマイズしていなければ、Javaランタイムはデフォルトの直列化メカニズムを使用してそのクラスのインスタンスをディスクに永続化します。
最初の段階でserialVersionUIDが設定されていない膨大な数のクラスを修正したいが、IntelliJ IdeaやEclipseのようなツールは乱数を生成し、たくさんのファイルでは動作しないので不十分一度に。私は次のbashスクリプト(Windowsユーザーには申し訳ありません。Macを購入するかLinuxに変換することを検討してください)を簡単に修正するために思いつきます。
base_dir=$(pwd)
src_dir=$base_dir/src/main/Java
ic_api_cp=$base_dir/target/classes
while read f
do
clazz=${f//\//.}
clazz=${clazz/%.Java/}
seruidstr=$(serialver -classpath $ic_api_cp $clazz | cut -d ':' -f 2 | sed -e 's/^\s\+//')
Perl -ni.bak -e "print $_; printf qq{%s\n}, q{ private $seruidstr} if /public class/" $src_dir/$f
done
このスクリプトを保存し、add_serialVersionUID.shを〜/ binに渡します。その後、MavenまたはGradleプロジェクトのルートディレクトリで次のように実行します。
add_serialVersionUID.sh < myJavaToAmend.lst
この.lstには、serialVersionUIDを追加するためのJavaファイルのリストが次の形式で含まれています。
com/abc/ic/api/model/domain/item/BizOrderTransDO.Java
com/abc/ic/api/model/domain/item/CardPassFeature.Java
com/abc/ic/api/model/domain/item/CategoryFeature.Java
com/abc/ic/api/model/domain/item/GoodsFeature.Java
com/abc/ic/api/model/domain/item/ItemFeature.Java
com/abc/ic/api/model/domain/item/ItemPicUrls.Java
com/abc/ic/api/model/domain/item/ItemSkuDO.Java
com/abc/ic/api/model/domain/serve/ServeCategoryFeature.Java
com/abc/ic/api/model/domain/serve/ServeFeature.Java
com/abc/ic/api/model/param/depot/DepotItemDTO.Java
com/abc/ic/api/model/param/depot/DepotItemQueryDTO.Java
com/abc/ic/api/model/param/depot/InDepotDTO.Java
com/abc/ic/api/model/param/depot/OutDepotDTO.Java
このスクリプトは内部でJDK serialVerツールを使用しています。そのため、$ Java_HOME/binがPATHにあることを確認してください。
この質問はJoshua BlochによるEffective Javaで非常によく文書化されています。とても良い本で、必ず読んでください。以下にいくつかの理由を概説します。
直列化ランタイムは、直列化可能クラスごとに、シリアルバージョンという番号を付けます。この番号はserialVersionUIDと呼ばれます。今この数の後ろにある数学があり、それはクラスで定義されているフィールド/メソッドに基づいて出てきます。同じクラスに対して、同じバージョンが毎回生成されます。この番号は、直列化されたオブジェクトの送信側と受信側が、直列化に関して互換性のあるそのオブジェクトのクラスをロードしたことを確認するために、直列化復元中に使用されます。受信側が、対応する送信側のクラスとは異なるserialVersionUIDを持つオブジェクトのクラスをロードした場合、逆シリアル化はInvalidClassExceptionになります。
クラスが直列化可能な場合は、static、final、およびlong型でなければならない「serialVersionUID」という名前のフィールドを宣言することによって、独自のserialVersionUIDを明示的に宣言することもできます。 EclipseのようなほとんどのIDEはあなたがその長い文字列を生成するのを助けます。
オブジェクトがシリアル化されるたびに、オブジェクトはオブジェクトのクラスのバージョンID番号でスタンプされます。このIDは serialVersionUID と呼ばれ、クラス構造に関する情報に基づいて計算されます。あなたがEmployeeクラスを作成し、それがバージョンID#333(JVMによって割り当てられた)を持っているとします。今度はそのクラスのオブジェクトをシリアライズするとき(Employeeオブジェクトと仮定します).
状況を考えてください - 将来あなたはあなたのクラスを編集または変更する必要があり、その場合それを修正するとき、JVMはそれに新しいUIDを割り当てます(#444と仮定します)。 object、JVMはシリアライズされたオブジェクト(Employeeオブジェクト)のバージョンID(#333)とクラスのもの、すなわち#444(変更されてから)を比較します。比較すると、JVMは両方のバージョンのUIDが異なるため、逆シリアル化は失敗します。したがって、各クラスのserialVersionIDがプログラマ自身によって定義されているとします。クラスが将来進化しても同じです。したがって、クラスが変更されても、JVMは常にそのクラスが直列化されたオブジェクトと互換性があることを検出します。詳細については、HEAD FIRST Javaの第14章を参照してください。
簡単な説明
1.データをシリアル化していますか。 シリアライゼーションは基本的にクラスデータをファイル/ stream/etcに書き込むことです。デシリアライゼーションはそのデータをクラスに読み戻すことです。
2.生産を開始する予定はありますか? 重要でない/偽のデータで何かをテストしているだけであれば、心配しないでください(直接シリアル化をテストしているのでない限り)。
3.これは最初のバージョンですか? そうであれば、serialVersionUID=1L
を設定してください。
4.これは2番目、3番目などのprodバージョンですか? serialVersionUID
について心配する必要があります、そしてそれを詳細に調べる必要があります。
基本的に、書き込み/読み込みが必要なクラスを更新するときにバージョンを正しく更新しないと、古いデータを読み込もうとするとエラーが発生します。
まず、クラスでSerialVersionUIDを宣言しないと、Javaランタイムによって生成されますが、そのプロセスは、フィールド数、フィールドの種類、フィールドのアクセス修飾子、実装されたインターフェイスなど、多くのクラスメタデータに影響されますしたがって、それを私たち自身で宣言することをお勧めします。Eclipseは同じことについて警告しています。
シリアライゼーション:私たちはしばしば状態(オブジェクトの変数内のデータ)が非常に重要で、電源やシステムの障害(またはオブジェクトの状態を他のユーザーに送信する場合のネットワーク障害)によって失われる危険がない重要なオブジェクトを扱います。機械。この問題に対する解決策は "Persistence"と呼ばれ、単にデータを永続化(保持/保存)することを意味します。シリアル化は、(データをディスク/メモリに保存することによって)永続性を実現するための他の多くの方法の1つです。オブジェクトの状態を保存するときは、オブジェクトを正しく読み返すことができるように、オブジェクトのIDを作成することが重要です(デシリアライゼーション)。この一意のIDはSerialVersionUIDです。
簡単に言うと、このフィールドはシリアライズされたデータを正しくデシリアライズできるかどうかを確認するために使用されます。シリアライゼーションとデシリアライゼーションはしばしばプログラムの異なるコピーによって行われます - 例えば、サーバーはオブジェクトを文字列に変換し、クライアントは受け取った文字列をオブジェクトに変換します。このフィールドは、両方ともこのオブジェクトが何であるかについて同じ考えで動作することを伝えます。このフィールドは以下の場合に役立ちます。
あなたはあなたのプログラムの多くの異なるコピーを異なる場所に持っています(1つのサーバーと100のクライアントのように)。あなたがあなたのオブジェクトを変更し、あなたのバージョン番号を変更し、そしてこのクライアントを更新することを忘れたら、それは彼が逆シリアル化の能力がないことを知るでしょう
あなたはあなたのデータをあるファイルに保存し、後であなたがそれを修正されたオブジェクトであなたのプログラムの更新されたバージョンでそれを開こうとする - あなたが正しいバージョンを保つならこのファイルは互換性がない
それはいつ重要ですか?
最も明白なことです - あなたがあなたのオブジェクトにいくつかのフィールドを追加するならば、それらはそれらのオブジェクト構造の中にこれらのフィールドを持っていないので、古いバージョンはそれらを使うことができません。
それほど明白ではない - あなたがオブジェクトを逆シリアル化するとき、文字列の中に存在しないフィールドはNULLとして保持されるでしょう。オブジェクトからフィールドを削除した場合、古いバージョンではこのフィールドはallways-NULLとして保持され、古いバージョンでこのフィールドのデータに依存していると誤動作する可能性があります(とにかくあなたが作成したものです。
自明ではない - ある分野の意味に入れる考えを変えることがある。たとえば、12歳のときは「自転車」の下で「自転車」を意味しますが、18歳のときは「オートバイ」を意味します - 友達が「市内を横断する自転車」に誘ってくれるのはあなただけです。自転車に乗ってきた、あなたはそれがフィールド間で同じ意味を維持することがいかに重要であるか理解しないでしょう:-)