serialVersionUIDとは何ですか?なぜそれを使用する必要があるのですか?


3000

serialVersionUIDがない場合、Eclipseは警告を出します。

直列化可能クラスFooは、long型の静的なfinal serialVersionUIDフィールドを宣言していません

それは何でserialVersionUID、なぜそれが重要なのですか?行方不明serialVersionUIDが問題を引き起こす例を示してください。


1
serialversionUIDに関する良い習慣を見つけてください。dzone.com/articles/what-is-serialversionuid
ceyun

回答:


2290

のドキュメントはjava.io.Serializable、おそらくあなたが得るのと同じくらい良い説明です:

シリアル化ランタイムは、シリアル化可能な各クラスにと呼ばれるバージョン番号を関連付けますserialVersionUID。これは、シリアル化されたオブジェクトの送信者と受信者が、そのオブジェクトに対して、シリアル化に関して互換性のあるクラスを読み込んだことを確認するために使用されます。受信serialVersionUID者が、対応する送信者のクラスとは異なるオブジェクトのクラスをロードした場合、逆シリアル化の結果はになり InvalidClassExceptionます。直列化可能クラスは、static、final、およびtypeである必要があるserialVersionUIDという名前のフィールドを宣言することにより、それ自体を明示的に宣言serialVersionUIDできますlong

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

直列化可能クラスがを明示的に宣言しない場合、直列serialVersionUID化ランタイムはserialVersionUID、Java(TM)オブジェクト直列化仕様で説明されているように、クラスのさまざまな側面に基づいてそのクラスのデフォルト値を計算します。ただし、デフォルトの計算は、コンパイラーの実装によって異なる可能性があるクラスの詳細に非常に敏感であり、逆シリアル化中に予期しない結果になる可能性があるため、すべての直列化可能クラスで値を明示的に宣言することを強くお勧めserialVersionUIDます。したがって、異なるJavaコンパイラーの実装全体で一貫した値を保証するには、シリアライズ可能なクラスで明示的な値を宣言する必要があります。また、明示的であることを強くお勧めしますserialVersionUIDInvalidClassExceptionsserialVersionUIDserialVersionUIDserialVersionUID宣言は、可能な場合はプライベート修飾子を使用します。そのような宣言は、すぐに宣言するクラスserialVersionUIDフィールドにのみ適用されるため、継承されたメンバーとしては役に立ちません。


326
つまり、本質的に言っているのは、ユーザーが上記のすべての内容を理解していなかったとしても、シリアル化について心配する必要はないとユーザーは言ったということです。「どうやって?」と答えたと思います。「理由」を説明するのではなく、1つには、なぜSerializableVersionUIDに悩まされたのか理解できません。
Ziggy、

366
理由は2番目の段落にあります。serialVersionUIDを明示的に指定しない場合、値は自動的に生成されますが、コンパイラの実装に依存するため、脆弱です。
Jon Skeet、

14
そしてなぜEclipseは「private static final long serialVersionUID = 1L;」が必要だと言っているのですか?例外クラスを拡張するとどうなりますか?
JohnMerlino 2014年

21
@JohnMerlino:まあ、それが1つ必要だとは思いませんが、例外を正しくシリアル化するために1つ提案しているかもしれません。それらをシリアル化しない場合は、定数は本当に必要ありません。
Jon Skeet

16
@JohnMerlino、なぜあなたの質問の理由の部分に答える:例外はSerializableを実装し、eclipseは、serialVersionUIDを設定していないことを警告します。輪郭。
zpon 2014年

475

実装のためにシリアル化する必要があるという理由だけでシリアル化している場合(HTTPSessionたとえば、のシリアル化を考慮している場合、それが格納されているかどうかに関係なく、おそらくde-serializingフォームオブジェクトは気にしません)。これは無視してください。

実際にシリアライゼーションを使用している場合は、シリアライゼーションを直接使用してオブジェクトを格納および取得することを計画している場合にのみ問題になります。はserialVersionUIDクラスのバージョンを表します。クラスの現在のバージョンが以前のバージョンと下位互換性がない場合は、バージョンを増やす必要があります。

ほとんどの場合、シリアル化を直接使用することはおそらくないでしょう。この場合は、SerialVersionUIDクイックフィックスオプションをクリックしてデフォルトを生成し、それを心配する必要はありません。


78
永続的なストレージにシリアル化を使用していない場合は、値を追加するのではなく、@ SuppressWarningsを使用する必要があると思います。クラスが煩雑になるのを防ぎ、serialVersionUIDメカニズムの機能を維持して、互換性のない変更からユーザーを保護します。
トムアンダーソン

25
1つの行(@SuppressWarningsアノテーション)を別の行(シリアライズ可能なID)とは対照的に「クラスの煩雑さを減らし」ているのではありません。永続的なストレージにシリアル化を使用していない場合は、なぜ「1」を使用しないのですか?その場合でも、自動生成されたIDはどうでも構いません。
MetroidFan2002

71
@ MetroidFan2002:serialVersionUID互換性のない変更からユーザーを保護するという@TomAndersonの主張は有効だと思います。@SuppressWarningsクラスを永続的なストレージに使用したくない場合は、ドキュメントを使用する方が意図がよくなります。
AbdullahC

11
「クラスの現在のバージョンが以前のバージョンとの下位互換性がない場合は、インクリメントする必要があります。」まず、シリアル化の広範なオブジェクトバージョン管理サポートを調べて、(a)クラスが実際にシリアル化非互換の方法であることを確認する必要があります。これは仕様ごとに達成するのが非常に困難です。(b)カスタムのread / writeObject()メソッド、readResolve / writeReplace()メソッド、serializableFields宣言などのスキームを試して、ストリームの互換性が維持されるようにします。実際の変更はserialVersionUID最後の手段であり、絶望の助言です。
ローンの侯爵

4
@EJPのserialVersionUIDの増分は、クラスの最初の作成者が明示的に導入したときに明らかになります。jvmで生成されたシリアルIDは問題ないはずです。これは私が連載で見た最良の答えです。
14

307

Josh Blochの著書「Effective Java(2nd Edition)」をプラグインするこの機会を逃すことはできません。第11章は、Javaシリアライゼーションに関する不可欠なリソースです。

Joshによると、自動生成されたUIDは、クラス名、実装されたインターフェース、およびすべてのパブリックメンバーと保護されたメンバーに基づいて生成されます。これらのいずれかを何らかの方法で変更すると、が変更されserialVersionUIDます。したがって、クラスのバージョンが1つしかシリアル化されない(プロセス間で、または後でストレージから取得される)ことが確実である場合にのみ、それらをいじる必要はありません。

今のところそれらを無視し、後でクラスを何らかの方法で変更する必要があるが古いバージョンのクラスとの互換性を維持する必要があることがわかった場合は、JDKツールserialverを使用serialVersionUIDして古いクラスにを生成し、明示的に設定することができます。新しいクラスで。(変更によっては、メソッドwriteObjectreadObjectメソッドを追加してカスタムシリアライゼーションを実装する必要がある場合もありますSerializable。javadocまたは前述の第11章を参照してください。)


33
それで、クラスの古いバージョンとの互換性が気になった場合、SerializableVersionUIDに悩まされるかもしれません。
Ziggy、

10
そうです、新しいバージョンがパブリックメンバーを保護されたものに変更した場合、デフォルトのSerializableVersionUIDは異なり、InvalidClassExceptionsが発生します。
Chander Shivdasani、2012年

3
クラス名、実装されたインターフェイス、すべてのパブリックメソッドと保護されたメソッド、すべてのインスタンス変数。
アコウ、2013年

31
Joshua Blochがすべての Serializableクラスに対してシリアルバージョンのuidを指定する価値があるとアドバイスしていることは注目に値します。第11章の引用: 選択したシリアル化形式に関係なく、作成するすべてのシリアル化可能クラスで明示的なシリアルバージョンUIDを宣言します。これにより、シリアルバージョンUIDが非互換性の原因となる可能性がなくなります(アイテム74)。小さなパフォーマンス上の利点もあります。シリアルバージョンのUIDが提供されていない場合、実行時に生成するためにコストのかかる計算が必要になります。
アシュトシュジンダル2014

136

これらのserialVersionUID警告を無視するようにEclipseに指示できます。

「ウィンドウ」>「設定」>「Java」>「コンパイラー」>「エラー/警告」>「潜在的なプログラミングの問題」

ご存知ない場合は、このセクションで有効にできる他の警告がたくさんあります(またはエラーとして報告される場合もあります)。多くは非常に便利です。

  • 潜在的なプログラミングの問題:偶発的なブール割り当ての可能性
  • 潜在的なプログラミングの問題:nullポインターアクセス
  • 不要なコード:ローカル変数が読み込まれることはありません
  • 不要なコード:冗長なnullチェック
  • 不要なコード:不要なキャストまたは「instanceof」

などなど。


21
賛成票を投じますが、元のポスターが何かを連載しているようには見えないからです。ポスターが「私はこれをシリアル化している...」と言った場合は、代わりに投票が行われます:P
ジョンガードナー

3
真実-私は質問者が警告されることを単に望んでいないと想定していました
matt b

14
@ガードナー->同意しました!しかし、質問者はまた、警告されたくない理由を知りたがっています。
Ziggy、

8
質問者は、なぜUIDが必要なのかを気にします。したがって、警告を無視するように彼に単に伝えることは、反対票を投じられるべきです。
cinqS 2017

111

serialVersionUIDシリアル化されたデータのバージョン管理を容易にします。その値は、シリアライズ時にデータとともに保存されます。逆シリアル化すると、同じバージョンがチェックされ、シリアル化されたデータが現在のコードとどのように一致するかが確認されます。

データをバージョン管理したい場合は、通常serialVersionUID、0 から始め、クラスの構造を変更するたびにバンプして、シリアル化されたデータを変更します(非一時フィールドの追加または削除)。

組み込みの逆シリアル化メカニズム(in.defaultReadObject())は、古いバージョンのデータからの逆シリアル化を拒否します。ただし、必要に応じて、古いデータを読み戻す独自のreadObject()関数を定義できます。次に、このカスタムコードはをチェックしてserialVersionUID、データがどのバージョンにあるかを確認し、それを逆シリアル化する方法を決定できます。このバージョン管理手法は、コードのいくつかのバージョンが残っているシリアル化されたデータを格納する場合に役立ちます。

しかし、シリアル化されたデータをそのような長い期間保存することはあまり一般的ではありません。シリアル化メカニズムを使用してデータを一時的にキャッシュなどに一時的に書き込むか、コードベースの関連する部分の同じバージョンを持つ別のプログラムにネットワーク経由で送信するのがはるかに一般的です。

この場合、下位互換性を維持する必要はありません。実際に通信しているコードベースが、関連するクラスの同じバージョンを持っていることを確認することだけに関心があります。このようなチェックを容易にするために、serialVersionUID以前と同じように維持し、クラスを変更するときにチェックを更新することを忘れないでください。

フィールドを更新するのを忘れた場合、異なる構造を持つが同じを持つ、クラスの2つの異なるバージョンになる可能性がありますserialVersionUID。これが発生した場合、デフォルトのメカニズム(in.defaultReadObject())は違いを検出せず、互換性のないデータのシリアル化解除を試みます。これで、不可解なランタイムエラーまたはサイレントエラー(nullフィールド)が発生する可能性があります。これらのタイプのエラーを見つけるのは難しいかもしれません。

したがって、このユースケースを支援するために、JavaプラットフォームではserialVersionUID手動で設定しないという選択肢があります。代わりに、クラス構造のハッシュがコンパイル時に生成され、IDとして使用されます。このメカニズムにより、同じIDで異な​​るクラス構造が存在することがなくなり、上記のトレースが困難なランタイムシリアル化の失敗が発生しなくなります。

しかし、自動生成されたID戦略には裏側があります。つまり、同じクラスに対して生成されたIDはコンパイラ間で異なる可能性があります(上記のJon Skeetが言及)。したがって、異なるコンパイラでコンパイルされたコード間でシリアル化されたデータを通信する場合は、とにかく手動でIDを維持することをお勧めします。

また、前述の最初の使用例のようにデータと下位互換性がある場合は、IDを自分で維持することもできます。これは、読み取り可能なIDを取得し、変更のタイミングと方法をより詳細に制御するためです。


4
非一時フィールドを追加または削除しても、クラスのシリアル化に互換性がありません。したがって、そのような変更に「バンプ」する理由はありません。
ローンの侯爵2014

4
@EJP:えっ?データを追加すると、私の世界のシリアル化データが確実に変わります。
Alexander Torstling 2014

3
@AlexanderTorstling私が書いたものを読んでください。「シリアライゼーションデータを変更しない」とは言いませんでした。私はそれが「クラスのシリアル化を非互換にすることはない」と述べました。同じではありません。オブジェクト直列化仕様のバージョン管理の章を読む必要があります。
ローンの侯爵

3
@EJP:非一時フィールドを追加しても、クラスをシリアル化非互換にする必要があるとは限りませんが、それは構造化された変更であり、シリアル化されたデータを変更します。後方互換性も扱います。これについては、この記事の後半でも説明します。正確にあなたのポイントは何ですか?
Alexander Torstling 2014年

4
私の主張は、私が言ったこととまったく同じです。非一時フィールドを追加または削除しても、クラスのシリアル化に互換性がありません。したがって、毎回serialVersionUIDをバンプする必要はありません。
ローンの侯爵

72

serialVersionUIDとは何ですか?なぜそれを使用する必要があるのですか?

SerialVersionUIDは各クラスの一意の識別子です。JVMこれを使用してクラスのバージョンを比較し、直列化中に同じクラスが使用されたことを確認して、直列化解除中に同じクラスがロードされるようにします。

1つを指定するとより多くの制御が得られますが、JVMは指定しなければ制御を生成します。生成される値は、コンパイラによって異なる場合があります。さらに、古いシリアル化されたオブジェクト[ backward incompatibility]の逆シリアル化を何らかの理由で禁止したい場合もあります。この場合は、serialVersionUIDを変更する必要があります。

say のjavadocSerializable

デフォルトのserialVersionUIDの計算は、コンパイラーの実装によって異なるクラスの詳細に非常に敏感であるため、InvalidClassException逆シリアル化中に予期しないが発生する可能性があります。

したがって、serialVersionUIDを宣言する必要があります。これにより、制御が強化されます。

この記事には、このトピックに関するいくつかの良い点があります。


3
@Vinothbabuですが、serialVersionUIDは静的なので、静的変数はシリアル化できません。次に、jvmは、逆シリアル化オブジェクトのバージョンが何であるかを知らずにバージョンをチェックする理由
Kranthi Sama

3
この回答で言及されていないことの1つは、serialVersionUID理由を知らずに盲目的に含めることにより、意図しない結果を引き起こす可能性があることです。トムアンダーソンのMetroidFan2002の回答に関するコメントは、次のように述べています。互換性のない変更からユーザーを保護するためのserialVersionUIDメカニズム。」
カービィ

7
serialVersionUID「各クラスの一意の識別子」ではありません。完全修飾クラス名はそれです。これは、あるバージョンインジケータ。
ローン侯爵、2015年

58

元の質問では、「なぜそれが重要なのか」と「これSerial Version IDが役立つ例」が求められました。さて、私はそれを見つけました。

Carクラスを作成し、インスタンス化して、オブジェクトストリームに書き出すとします。平坦化された自動車オブジェクトは、しばらくの間ファイルシステムに存在します。一方、Car新しいフィールドを追加してクラスを変更した場合。後で、平坦化されたCarオブジェクトを読み取ろうとする(つまり、逆シリアル化する)と、java.io.InvalidClassExceptionすべてのクラスに自動的に一意の識別子が与えられるためれます。この例外は、クラスの識別子がフラット化されたオブジェクトの識別子と等しくない場合にスローされます。本当に考えれば、新しいフィールドが追加されたため、例外がスローされます。明示的なserialVersionUIDを宣言してバージョン管理を自分で制御することで、この例外がスローされるのを回避できます。明示的に宣言することで、パフォーマンス上の小さな利点もあります。serialVersionUID(計算する必要がないため)。そのため、次に示すように、作成した直後に独自のserialVersionUIDをSerializableクラスに追加することをお勧めします。

public class Car {
    static final long serialVersionUID = 1L; //assign a long value
}

そして、すべてのUIDに1Lではなくランダムな長い数字を割り当てる必要があります。
abbas 2017年

4
@abbasなぜそうすべきなのか?どのような違いがあるか説明してください。
ローン侯爵

名前が示すように、オブジェクトのシリアル化に使用されたクラスのバージョンを表します。毎回同じ番号を割り当てると、デシリアライズするための適切なクラスを見つけることができなくなります。したがって、クラスを変更するときはいつでも、バージョンも変更することをお勧めします。
abbas 2017年

2
@abbas、この意図は、インクリメントする自然数の使用などと衝突しません1
Vadzim 2017

2
@BillK、シリアル化チェックはクラス名とserialVersionUIDのペアにバインドされていると思いました。したがって、さまざまなクラスやライブラリのさまざまな番号付け方式が干渉することはありません。それとも、コード生成ライブラリを暗示しましたか?
Vadzim 2018年

44

最初に、シリアル化とは何かを説明する必要があります。

シリアル化により、オブジェクトをストリームに変換して、そのオブジェクトをネットワーク経由で送信したり、ファイルに保存したり、DBに保存して文字を使用したりできます。

シリアライゼーションにはいくつかのルールがあります

  • オブジェクトが直列化可能であるのは、そのクラスまたはそのスーパークラスがSerializableインターフェースを実装している場合のみです

  • スーパークラスがそうでなくても、オブジェクトは直列化可能です(それ自体がSerializableインターフェースを実装します)。ただし、Serializableインターフェースを実装しない、直列化可能クラスの階層の最初のスーパークラスには、引数なしのコンストラクターが必要です。これに違反した場合、readObject()は実行時にjava.io.InvalidClassExceptionを生成します

  • すべてのプリミティブ型はシリアライズ可能です。

  • 一時フィールド(transient修飾子付き)はシリアライズされません(つまり、保存または復元されません)。Serializableを実装するクラスは、シリアル化をサポートしないクラス(ファイルストリームなど)の一時フィールドをマークする必要があります。

  • 静的フィールド(静的修飾子付き)はシリアライズされません。

ときにObjectシリアライズされ、Javaランタイムは、別名シリアルバージョン番号を関連付けますserialVersionID

serialVersionIDが必要な場合: シリアル化に関して送信側と受信側に互換性があることを確認するための逆シリアル化中。レシーバーが別のクラスをロードした場合、serialVersionID逆シリアル化はで終了しInvalidClassCastExceptionます。
直列化可能クラスは、static、final、およびlong型である必要がある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)

私が間違っている場合は修正してください-ローカルクラスはクラスパスで現在使用/使用しているクラスであり、ストリームは別のパーティ(たとえば、回答を返し、応答)。この状況は、サードパーティのライブラリを更新したサーバーと通信しているときに発生する可能性がありますが、あなた(クライアント)はこれを実行していません。
ビクター

37

オブジェクトをバイト配列にシリアル化して送信/保存する必要がない場合は、心配する必要はありません。その場合、オブジェクトのデシリアライザがクラスローダーが持つオブジェクトのバージョンと一致させるため、serialVersionUIDを考慮する必要があります。詳細については、Java言語仕様を参照してください。


10
オブジェクトをシリアル化しない場合、なぜそれらはSerializableなのですか?
エリクソン2008年

7
@erickson-親クラスは、ArrayListなどのシリアル化可能ですが、独自のオブジェクト(たとえば、変更された配列リスト)でそれをベースとして使用したいが、作成したコレクションをシリアル化することはありません。
MetroidFan2002 2009年

5
これは、Java言語仕様のどこにも記載されていません。これは、オブジェクトのバージョニング仕様で言及されています。
ローンの侯爵2014

4
これは、Java 8 Object Versioning Specificationへのリンクです。
バジルブルク2014年

34

クラスでこの警告が表示された場合、シリアル化について考えたことはなく、自分で宣言しなかったことを 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メソッドを追加するだけです。この場合は警告は出ないはずです(通常、クラスの異なるバージョン間でこのような匿名クラスを信頼性の高い方法でシリアル化および逆シリアル化することはできないため)が、コンパイラがこれをどのように認識できるかはわかりません。


4
特に、Listのようなクラスについて議論するときは、継承よりも構成の方が理にかなっていると思います。ただし、多くのフレームワークでは、シリアライズ可能な抽象スーパークラス(Struts 1.2のActionFormクラス、またはSaxonのExtensionFunctionDefinitionなど)から拡張する必要があります。この場合、このソリューションは実行できません。私はあなたが正しいと思います、特定の場合に警告が無視された方がいいでしょう(抽象シリアライズ可能なクラスから拡張した場合など)
piepera

2
クラスを継承するのではなく、メンバーとして追加する場合は、使用するメンバークラスのEVERYメソッドのラッパーメソッドを作成する必要があります。これにより、多くの状況でそれを実行できなくなります。 Javaがperlに似た関数を除いて、__AUTOLOAD私にはわかりません。
M_M 2012

4
@M_M:多くのメソッドをラップされたオブジェクトに委譲する場合、もちろん委任を使用することは適切ではありません。しかし、このケースは設計ミスの兆候だと思います。クラスのユーザー(たとえば、「MainGui」)は、ラップされたオブジェクト(JFrameなど)の多くのメソッドを呼び出す必要はありません。
パウロEbermann

1
委任について私が気に入らないのは、委任への参照を保持する必要があることです。そして、すべての参照はより多くのメモリを意味します。私が間違っていたら訂正してください。100個のオブジェクトのCustomizedArrayListが必要な場合、これはそれほど重要ではありませんが、少数のオブジェクトの何百ものCustomizdeArrayListsが必要な場合、メモリ使用量が大幅に増加します。
WVrock 2014年

2
Throwableはシリアル化可能であり、Throwableのみがスロー可能であるため、シリアル化可能でない例外を定義することはできません。委任はできません。
Phil

22

フィールドserialVersionUIDの重要性を理解するには、Serialization / Deserializationの仕組みを理解する必要があります。

Serializableクラスオブジェクトがシリアル化されると、Javaランタイムはシリアルバージョン番号(serialVersionUIDと呼ばれる)をこのシリアル化されたオブジェクトに関連付けます。このシリアル化されたオブジェクトを逆シリアル化するときに、Javaランタイムは、シリアル化されたオブジェクトのserialVersionUIDをクラスのserialVersionUIDと一致させます。両方が等しい場合は、逆シリアル化の次のプロセスのみが続行され、そうでない場合はInvalidClassExceptionがスローされます。

したがって、シリアライゼーション/デシリアライゼーションプロセスを成功させるには、シリアライズされたオブジェクトのserialVersionUIDがクラスのserialVersionUIDと同等である必要があると結論付けます。プログラマーがプログラムでserialVersionUID値を明示的に指定した場合、同じ値がシリアル化されたオブジェクトとクラスに関連付けられます。シリアル化と逆シリアル化のプラットフォームに関係なく(例:シリアル化は、WindowsのようなプラットフォームでsunまたはMS JVMとデシリアライゼーションは、Zing JVMを使用する別のプラットフォームLinuxにある場合があります)。

ただし、プログラマがserialVersionUIDを指定していない場合、オブジェクトのSerialization \ DeSerializationを実行している間、Javaランタイムは独自のアルゴリズムを使用してそれを計算します。このserialVersionUID計算アルゴリズムは、JREによって異なります。オブジェクトがシリアル化される環境が1つのJRE(例:SUN JVM)を使用していて、逆シリアル化が発生する環境がLinux Jvm(zing)を使用している可能性もあります。このような場合、シリアル化されたオブジェクトに関連付けられたserialVersionUIDは、逆シリアル化環境で計算されたクラスのserialVersionUIDとは異なります。逆シリアル化は成功しません。そのため、このような状況/問題を回避するには、プログラマは常にSerializableクラスのserialVersionUIDを指定する必要があります。


2
アルゴリズムは変わりませんが、仕様がわずかに不足しています。
ローン侯爵、

...アルゴリズムは変わりませんが、仕様がわずかに不足しています...つまり、jvmが変わる可能性があるということです... @ user207421
AnthonyJClink

18

気にしないでください。デフォルトの計算は非常に適切で、99,9999%のケースで十分です。そして、問題が発生した場合、(すでに述べたように)ニーズが発生したときにUIDを導入できます(これはほとんどありません)。


2
ごみ。クラスが変更されていない場合に適しています。「99.9999%」をサポートする証拠はありません。
ローン侯爵

18

欠落しているserialVersionUIDが問題を引き起こす可能性がある例については、次のとおりです。

モジュールを使用するWebモジュールで構成されるこのJava EEアプリケーションに取り組んでいEJBます。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

16

私は通常serialVersionUID、1つのコンテキストで使用します。JavaVMのコンテキストから離れることがわかっている場合。

私が使用したときに私はこれを知っているだろうObjectInputStreamし、ObjectOutputStream自分のアプリケーションのためか、私が知っていれば、ライブラリ/フレームワークIの使用は、それを使用します。serialVersionIDは、さまざまなバージョンまたはベンダーのさまざまなJava VMが正しく相互運用することを保証します。たとえばHttpSession、VMの外部に保存および取得された場合、アプリケーションサーバーの再起動およびアップグレード中にもセッションデータが残る可能性があります。

他のすべてのケースでは、私は使用します

@SuppressWarnings("serial")

ほとんどの場合、デフォルトserialVersionUIDで十分です。これにはExceptionHttpServlet


入れ替え可能なコンテナにはHttpServletは含まれません。たとえば、RMIの例外は含まれません。
ローン侯爵

14

フィールドデータは、クラスに格納されているいくつかの情報を表します。クラスはSerializableインターフェースを実装するので、serialVersionUIDフィールドを宣言するためのEclipseが自動的に提供されます。そこに設定された値1から始めましょう。

警告が表示されないようにするには、次のようにします。

@SuppressWarnings("serial")

11

CheckStyleが、Serializableを実装するクラスのserialVersionUIDが適切な値を持っていること、つまり、シリアルバージョンIDジェネレーターが生成するものと一致することを確認できると便利です。たとえば、多数のシリアル化可能なDTOを含むプロジェクトがある場合、既存のserialVersionUIDを削除して再生成することを思い出すのは面倒で、現在これを確認する唯一の方法(私が知っている)は、クラスごとに再生成して比較することです。古いもの。これは非常に痛いです。


8
シリアルジェネレーターが常にジェネレーターが生成するのと同じ値に設定されている場合、実際には必要ありません。結局のところ、その存在理由は、クラスがまだ互換性がある場合、変更後も同じままであることです。
パウロEbermann

4
その理由は、異なるコンパイラが同じクラスに対して同じ値を出すためです。javadocs(上記でも回答)で説明されているように、生成されたバージョンは壊れやすく、クラスが適切に逆シリアル化可能であっても異なる場合があります。このテストを毎回同じコンパイラで実行する限り、安全です。あなたがコードを変更していなくても、jdkをアップグレードして新しいルールが出てきたら、神が助けてくれます。
Andrew Backer

2
serialver生成されるものと一致させる必要はありません。-1
ローン侯爵

一般に、それはまったく必要ありません。@AndrewBackerの場合、同じ.javaファイル上に2つの異なるコンパイラが必要です。両方のバージョンの.classファイルが互いに通信します。ほとんどの場合、クラスを作成して配布するだけです。その場合は、SUIDがなくても問題ありません。
ビルK

1
ストレージ/取得の目的で本当にシリアライゼーションを使用する人は、通常、serialVersionUID1 に設定します。クラスの新しいバージョンに互換性がなくても、古いデータを処理できる必要がある場合は、バージョン番号を増やし、特別なコードを追加します古いフォーマットを扱います。serialVersionUID乱数(役に立たない)であったか、クラスが10を超える異なるバージョンを処理する必要があるため、1桁を超える数字が表示されるたびに泣きます。
john16384

11

SerialVersionUIDは、オブジェクトのバージョン管理に使用されます。クラスファイルでserialVersionUIDを指定することもできます。serialVersionUIDを指定しないことの結果は、クラスのフィールドを追加または変更すると、新しいクラスと古いシリアル化オブジェクトに対して生成されたserialVersionUIDが異なるため、既にシリアル化されたクラスが回復できないことです。Javaシリアル化プロセスは、シリアル化されたオブジェクトの状態を回復するために正しいserialVersionUIDに依存し、serialVersionUIDが一致しない場合はjava.io.InvalidClassExceptionをスローします

詳しくはこちら:http : //javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ


9

なぜJavaのSerialVersionUID内部Serializableクラスを使用するのですか?

serialization実行中、Javaランタイムはクラスのバージョン番号を作成して、後で逆シリアル化できるようにします。このバージョン番号はSerialVersionUID、Javaと同じです。

SerialVersionUIDシリアル化されたデータのバージョン管理に使用されます。SerialVersionUIDシリアル化されたインスタンスと一致する場合にのみ、クラスを逆シリアル化できます。SerialVersionUIDクラスで宣言しない場合、Javaランタイムが生成しますが、お勧めしません。デフォルトのメカニズムを回避するためにSerialVersionUIDprivate static final long変数として宣言することをお勧めします。

Serializableマーカーインターフェイスを実装してクラスを宣言すると、インターフェイスjava.io.Serializableを使用してプロセスをカスタマイズしていなければ、Javaランタイムはデフォルトのシリアル化メカニズムを使用して、そのクラスのインスタンスをディスクに永続化しExternalizableます。

JavaのSerializableクラス内でSerialVersionUIDを使用する理由もご覧ください。


8

古いクラスとの互換性を維持しながら、最初にserialVersionUIDが設定されていない膨大な数のクラスを修正したい場合、IntelliJ Idea、Eclipseなどのツールは乱数を生成し、ファイルの束で機能しないため不十分です一度に。次のbashスクリプト(Windowsユーザーには申し訳ありませんが、Macを購入するかLinuxに変換することを検討してください)を考えて、serialVersionUIDの問題を簡単に修正します。

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

このスクリプトを保存すると、〜/ binにadd_serialVersionUID.shと言います。次に、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に含まれていることを確認してください。


2
私にアイデアを与えます:リリースする前に、このようなツールでシリアルバージョンのuidを常に手動で再生成してください-そうすることで、実際のバージョンのためにシリアルバージョンのuidが変更されているはずのクラスに変更を加えることを忘れないでください互換性のない変更。それを手動で追跡することは非常に困難です。
イグナツィオ2016年

6

この質問は、Joshua Blochによる「Effective Java」に非常によく文書化されています。とても良い本で必読です。以下にいくつかの理由を概説します。

シリアライゼーションランタイムには、シリアル化可能なクラスごとにシリアルバージョンと呼ばれる番号が表示されます。この番号は、serialVersionUIDと呼ばれます。これで、この数値の背後にある数学があり、クラスで定義されているフィールド/メソッドに基づいて計算されます。同じクラスの場合、毎回同じバージョンが生成されます。この番号は、逆シリアル化中に、シリアル化されたオブジェクトの送信者と受信者が、そのオブジェクトに対して、シリアル化に関して互換性のあるクラスを読み込んだことを確認するために使用されます。レシーバーが、対応するセンダーのクラスとは異なるserialVersionUIDを持つオブジェクトのクラスをロードした場合、デシリアライズはInvalidClassExceptionになります。

クラスがシリアル化可能である場合、静的で、最終的な、long型の「serialVersionUID」という名前のフィールドを宣言することにより、独自のserialVersionUIDを明示的に宣言することもできます。EclipseなどのほとんどのIDEは、その長い文字列を生成するのに役立ちます。


6

オブジェクトがシリアル化されるたびに、オブジェクトにはオブジェクトのクラスのバージョンID番号がスタンプされます。このIDはserialVersionUIDと呼ば、クラス構造に関する情報に基づいて計算されます。Employeeクラスを作成し、バージョンIDが#333(JVMによって割り当てられている)であるとします。次に、そのクラスのオブジェクト(Suppose Employeeオブジェクト)をシリアル化すると、JVMはそれにUIDを#333として割り当てます。

状況を検討してください。将来、クラスを編集または変更する必要があり、その場合、クラスを変更すると、JVMが新しいUIDを割り当てます(仮定#444)。これで、従業員オブジェクトを逆シリアル化しようとすると、JVMはシリアル化されたオブジェクト(従業員オブジェクト)のバージョンID(#333)をクラスのバージョンID#444と比較します(変更されたため)。比較すると、JVMは両方のバージョンのUIDが異なるため、逆シリアル化は失敗します。したがって、各クラスのserialVersionIDがプログラマー自身によって定義されている場合。クラスが将来進化しても同じであり、したがって、クラスが変更されても、JVMは常にそのクラスがシリアル化されたオブジェクトと互換性があることを検出します。詳細については、HEAD FIRST JAVAの第14章を参照してください。


1
オブジェクトのクラスがシリアル化されるたびに、serialVersionUID送信されます。すべてのオブジェクトと一緒に送信されるわけではありません。
ローン侯爵

3

簡単な説明:

  1. データをシリアル化していますか?

    シリアライゼーションとは、基本的にクラスデータをファイル/ストリームなどに書き込むことです。逆シリアル化とは、そのデータをクラスに読み込むことです。

  2. 生産に入るつもりですか?

    重要でないデータや偽のデータを使用して何かをテストしているだけの場合は、心配しないでください(直接シリアル化をテストしている場合を除く)。

  3. これは最初のバージョンですか?

    その場合は、を設定しserialVersionUID=1Lます。

  4. これは2番目、3番目などの製品バージョンですか?

    今、あなたはについて心配する必要がありserialVersionUID、それを詳細に調べる必要があります。

基本的に、書き込み/読み取りが必要なクラスを更新するときにバージョンを正しく更新しないと、古いデータを読み取ろうとするとエラーが発生します。


2

'serialVersionUID'は、逆シリアル化プロセス中にクラスを一意に識別するために使用される64ビットの数値です。オブジェクトをシリアル化すると、クラスのserialVersionUIDもファイルに書き込まれます。このオブジェクトを逆シリアル化するたびに、Javaランタイムはシリアル化されたデータからこのserialVersionUID値を抽出し、クラスに関連付けられた同じ値を比較します。両方が一致しない場合、「java.io.InvalidClassException」がスローされます。

シリアル化可能なクラスがserialVersionUIDを明示的に宣言していない場合、シリアル化ランタイムは、フィールド、メソッドなどのクラスのさまざまな側面に基づいて、そのクラスのserialVersionUID値を計算します。デモアプリケーションについては、このリンクを参照してください。


1

簡単に言えば、このフィールドは、シリアル化されたデータを正しく逆シリアル化できるかどうかを確認するために使用されます。シリアル化と非シリアル化は、多くの場合、プログラムの異なるコピーによって行われます。たとえば、サーバーはオブジェクトを文字列に変換し、クライアントは受信した文字列をオブジェクトに変換します。このフィールドは、両方がこのオブジェクトが何であるかについて同じ考えで動作することを示します。このフィールドは次の場合に役立ちます。

  • あなたはあなたのプログラムの多くの異なるコピーを異なる場所に持っています(1つのサーバーと100のクライアントのような)。オブジェクトを変更し、バージョン番号を変更し、このクライアントの1つを更新するのを忘れた場合は、彼が逆シリアル化できないことを認識します

  • データを何らかのファイルに保存し、後でオブジェクトを変更したプログラムの更新バージョンでそれを開こうとした場合-バージョンを正しく維持すると、このファイルに互換性がないことがわかります

それはいつ重要ですか?

最も明白です-オブジェクトにいくつかのフィールドを追加した場合、古いバージョンではオブジェクト構造にこれらのフィールドがないため、それらを使用できません。

あまり明確でない-オブジェクトを逆シリアル化すると、文字列に存在しないフィールドはNULLとして保持されます。オブジェクトからフィールドを削除した場合、古いバージョンはこのフィールドを常にNULLのままにするため、古いバージョンがこのフィールドのデータに依存している場合、誤動作につながる可能性があります(とにかく、楽しみのためだけでなく、何かのために作成したものです:-))。

自明ではない-ある分野の意味に入れるアイデアを変更することがあります。たとえば、12歳のときは「自転車」の下の「自転車」を意味しますが、18歳のときは「オートバイ」を意味します-友達が「街を自転車で走る」ようにあなたを招待し、あなただけが1人になります自転車に乗ったら、分野を超えて同じ意味を保つことがどれほど重要であるか理解できます:-)


1

最初にあなたの質問に答えるために、クラスでSerialVersionUIDを宣言しない場合、Javaランタイムはそれを生成しますが、そのプロセスは、フィールドの数、フィールドのタイプ、フィールドのアクセス修飾子、実装されたインターフェイスを含む多くのクラスメタデータの影響を受けますクラスごとなど。したがって、自分で宣言することをお勧めします。Eclipseは同じことを警告しています。

シリアライゼーション:状態(オブジェクトの変数のデータ)が非常に重要である重要なオブジェクトを使用することがよくあります。そのため、オブジェクトの状態を他のオブジェクトに送信する場合に、電源/システムの障害(または)ネットワークの障害が原因でデータを失うリスクはありません。機械。この問題の解決策は「永続性」と呼ばれ、データを永続化(保持/保存)することを意味します。シリアル化は、(データをディスク/メモリに保存することにより)永続性を実現する他の多くの方法の1つです。オブジェクトの状態を保存するときは、オブジェクトを正しく読み取ることができるように、オブジェクトのIDを作成することが重要です(逆シリアル化)。この一意のIDは、ID is SerialVersionUIDです。


0

SerialVersionUIDとは何ですか?回答:-HQとODCの2人がそれぞれシリアライゼーションとデシリアライゼーションをそれぞれ実行するとします。この場合、ODCにいる受信者が認証された人物であることを認証するために、JVMはSerialVersionUIDと呼ばれる一意のIDを作成します。

これはシナリオに基づいた素晴らしい説明です、

なぜSerialVersionUIDなのか?

シリアル化:直列化の時点では、すべてのオブジェクトの送信側でJVMが一意識別子を保存します。JVMは、送信側システムに存在する対応する.classファイルに基づいてその一意のIDを生成する責任があります。

デシリアライズ:デシリアライゼーションの際に、受信側JVMはまた、受信機システム内に存在する対応する.classファイルに基づいて一意のIDを作成するローカルクラス固有ID、すなわちJVMを持つオブジェクトに関連付けられた一意のIDを比較します。両方の一意のIDが一致した場合、逆シリアル化のみが実行されます。そうでない場合は、InvalidClassExceptionと言うランタイム例外が発生します。この一意の識別子はSerialVersionUIDにすぎません

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.