単純な1Lではなく、長いserialVersionUIDを生成するのはなぜですか?


210

クラスがEclipseでSerializableを実装する場合、2つのオプションがあります。デフォルトを追加するserialVersionUID(1L)か、生成するかserialVersionUID(3567653491060394677L)です。最初の方がクールだと思いますが、多くの場合、2番目のオプションを使用している人を見かけました。生成する理由はありますlong serialVersionUIDか?


49
正確にどのように重複していますか?なぜそれを生成するのかは尋ねませんが、なぜ長いserialVersionUIDを生成するのでしょうか。
IAdapter 2009年

13
Jon SkeetがserialVersionUIDを使用する場合、彼は0Lを使用します:stackoverflow.com/questions/605828/… ;)
Hanno Fietz

8
@HannoFietz:正確な文は次のとおりです:「簡単にするために、必要に応じて0 から始めて、1ずつ増やすことをお勧めします。」それで、彼は0L最初だけ使用するように聞こえます。
またはMapper

7
@ORMapper:Jon Skeetが以前に戻って彼が書いたコードを更新する必要があることを意味していますか?構造的な非互換性の点までです。ギャスプ!異端!
Thilo、2015年

回答:


90

私の知る限り、これは以前のリリースとの互換性のためだけのものです。これは、以前にserialVersionUIDの使用を怠り、互換性があるはずであるがシリアル化が機能しなくなる原因となる変更を加えた場合にのみ役立ちます。

詳細については、Java Serialization Specを参照してください。


69

シリアル化バージョンUIDの目的は、オブジェクトの有効なシリアル化を実行するために、クラスのさまざまなバージョンを追跡することです。

アイデアは、クラスの特定のバージョンに固有のIDを生成することです。このIDは、シリアル化されたオブジェクトの構造に影響を与える新しいフィールドなど、クラスに新しい詳細が追加されたときに変更されます。

常に同じIDを使用します。たとえば1L、将来、クラス定義が変更されてシリアル化されたオブジェクトの構造が変更された場合、オブジェクトを逆シリアル化しようとしたときに問題が発生する可能性が高くなります。

IDを省略した場合、Javaはオブジェクトのフィールドに基づいて実際にIDを計算しますが、これはコストのかかるプロセスであるため、手動で提供するとパフォーマンスが向上します。

以下に、クラスのシリアル化とバージョン管理について説明する記事へのリンクをいくつか示します。


50
1Lを使用する背後にある考え方は、クラスのプロパティまたはメソッドを変更するたびに1Lを増やすことです。
パワーロード2009年

39
serialversionUIDが自動的に生成されるようにしても、実行時のパフォーマンスへの影響はありません。javacによってコンパイル時に生成されます。クラスのバイトコードを逆コンパイルすると、実際には変数がバイトコードに静的に表示されます。
Jared、

11
もう1つの注意点は、番号を明示的に管理することで、クラスの定義を完全に同じにする必要はなく、クラスのバージョンを「互換性がある」と見なすタイミングを決定することです。
スコットスタンフィールド

23
@ Jared Josh Blochの効果的なJavaの項目75に従って:第2版: "シリアル化可能なすべてのクラスで、明示的なシリアルバージョンUIDを宣言します。 」
コリンK

12
これはデフォルトのserialVersionUIDが推奨されていない主な理由のようです@coobird Note - It is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected serialVersionUID conflicts during deserialization, causing deserialization to fail. 上記のコメントは、Javaオブジェクト直列化仕様バージョン6.0から撮影された
user624558

20

生成されたものの主な理由は、すでに永続化されたコピーを持つクラスの既存のバージョンと互換性を持たせることです。


1
OKですが、いつも1L持っていれば同じです。変更を加えても、すべて互換性があります。
grep、2015年

@grepでフィールドの名前を変更してから、何が起こるかを確認してください。
Trejkaz

1
@grepポイントは、以前にserialVersionUIDを省略したクラスがある場合、生成されたものを自動的に取得することです。したがって、明示的に設定を開始する必要があります。1Lに設定すると、既存のクラスとの互換性がなくなりますが、生成された値を使用すると互換性が維持されます。
marc82ch 2016

15

の「長い」デフォルトは、Java Serialization SpecificationserialVersionUID定義されているデフォルト値であり、デフォルトのシリアル化動作から計算されます。

したがって、デフォルトのバージョン番号を追加すると、構造的に何も変更されていない限り、クラスは(デ)シリアル化を高速化しますが、クラスを変更する(フィールドを追加/削除する)場合は、シリアルナンバー。

既存のビットストリームと互換性がある必要がない場合は、1Lそこに配置し、何かが変更されたときに必要に応じてバージョンをインクリメントできます。つまり、変更されたクラスのデフォルトのシリアル化バージョンが、古いクラスのデフォルトバージョンと異なる場合です。


11

を実装するクラスを定義するたびに、serialVersionUIDを作成する必要がありますjava.io.Serializable。そうでない場合は、自動的に作成されますが、これは悪いことです。自動生成されたserialVersionUIDはクラスのメソッドシグネチャに基づいているため、将来クラスを変更してメソッドを追加した場合(たとえば)、クラスの「古い」バージョンのデシリアライズは失敗します。起こり得ることは次のとおりです。

  1. serialVersionUIDを定義せずに、クラスの最初のバージョンを作成します。
  2. クラスのインスタンスを永続ストアにシリアル化します。serialVersionUIDが自動的に生成されます。
  3. クラスを変更して新しいメソッドを追加し、アプリケーションを再デプロイします。
  4. 手順2でシリアル化されたインスタンスを逆シリアル化しようとしましたが、自動生成されたserialVersionUIDが異なるため、失敗します(成功する必要がある場合)。

1
実際、古いバージョンのクラスを逆シリアル化すると、同じではなくなるため、実際に失敗するはずです。クラスシグネチャが変更されたときにシリアル化の失敗を防ぐために、自分でserialVersionUIDを生成することをお勧めします。あなたの提案は適切ですが、その目的の説明は単に間違っており、誤解を招くものです。答えを修正するのが賢明でしょう。
mostruash 2013

6

serialVersionUIDを指定しない場合、Javaはその場でそれを作成します。生成されたserialVersionUIDはその番号です。クラスを以前のシリアル化されたバージョンと実際に互換性がないように変更したものの、ハッシュを変更した場合は、生成された非常に大きな数のserialVersionUID(またはエラーメッセージからの「予期される」番号)を使用する必要があります。 。それ以外の場合、自分ですべてを追跡している場合は、0、1、2 ...のほうが適切です。


==> 1を意味しました。クラスのさまざまな変更に互換性を持たせたい場合は、生成されたものを使用してください。2.異なるバージョンのクラスに互換性を持たせたくない場合は、デフォルトのバージョンを使用し、増分する場合は慎重に行ってください。私はそれを正しく理解しましたか?
JavaDeveloper 2014年

4

serialVersionUID(3567653491060394677L)を生成するのではなく、serialVersionUID(1L)を使用すると、何か言っていることになります。

バージョン番号が1であるこのクラスの互換性のないシリアル化されたバージョンを持つこのクラスに影響を与えるシステムがないことを100%確信していると言っています。

シリアル化されたバージョン履歴が不明であるという言い訳を考えることができれば、自信を持って言うのは難しいかもしれません。その生涯において、成功したクラスは多くの人々によって維持され、多くのプロジェクトに住み、多くのシステムに常駐します。

あなたはそれに苦しむことができます。または、負けることを期待して宝くじをすることもできます。バージョンを生成した場合、問題が発生する可能性はほとんどありません。「ねえ、まだ誰も1を使っていないと思う」と思ったら、オッズは小さいよりも大きいです。0と1はクールだと思うので、ヒット率が高くなります。

-

serialVersionUID(1L)を使用するのではなく、serialVersionUID(3567653491060394677L)を生成すると、何か言っていることになります。

このクラスの歴史上、他のバージョン番号が手動で作成または生成された可能性があると言っていて、Longsは大きな数をおかしくしているので気にしません。

どちらの方法でも、クラスが存在する、または存在する可能性のある全宇宙でクラスをシリアル化するときに使用されるバージョン番号の履歴を完全に知らない限り、チャンスをつかんでいます。1がAOKであることを100%確認する時間があれば、それを試してください。それで十分な場合は、先に進んでやみくもに数を生成してください。抽選に失敗するよりも、宝くじに当たる可能性が高くなります。もしそうなら、私に知らせて、私はあなたにビールを買います。

宝くじをプレイするこのすべての話で、serialVersionUIDがランダムに生成されるという印象をあなたに与えたかもしれません。実際、数値の範囲がLongのすべての可能な値に均等に分散されている限り、問題はありません。ただし、実際には次のように行われます。

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

これで得られる唯一の違いは、ランダムソースが必要ないことです。クラス自体の変更を使用して結果を変更しています。しかし、鳩の巣の原理によれば、それがうまくいかず、衝突を起こす可能性はまだあります。それは信じられないほど信じられないほどです。ビールを一杯飲んで頑張ってね。

ただし、クラスが1つのシステムと1つのコードベースにしか存在しない場合でも、手動で数値を増やすと衝突の可能性がゼロになると考えると、人間を理解できなくなります。:)


「システム」がクラスに触れる場合、つまり、シリアライゼーションが非互換になるようにクラスを変更する場合、問題は、そのシステムもserialVersionUIDを変更するかどうかです。私は、オッズが長いときに変更することを覚えているよりも小さいとは思いません。むしろその逆が正しいと思います。覚えやすい数字の方が変更が多いほど、誤って変更していないことに気づきました。
RetoGmür12年

2
これは誤りです!serialVersionUIDを生成し、1Lや実際に何も言っているのではなく、ソースコードでその値を宣言した場合:未定義の影響による将来検出されない可能性のある衝突が必要で​​あり、Javaや人間がこれを防止したくない。Javaは偏執的ですが従順です。人間は通常、大きな数を台無しにしないでください。このように、クラスが変更されても、javaは互換性のない古いバージョンのクラスを逆シリアル化できます。MwoaHaHa ...;)
Superole 2014

1

まあ、serialVersionUIDは、「静的フィールドはシリアル化されない」というルールの例外です。ObjectOutputStreamは、serialVersionUIDの値を出力ストリームに書き込むたびに書き込みます。ObjectInputStreamはそれを読み取り、ストリームから読み取られた値がクラスの現在のバージョンのserialVersionUID値と一致しない場合、InvalidClassExceptionをスローします。さらに、シリアル化されるクラスで正式に宣言されたserialVersionUIDがない場合、コンパイラーは、クラスで宣言されたフィールドに基づいて生成された値を使用して、それを自動的に追加します。


0

多くの場合、デフォルトのIDは一意ではないからです。ユニークなコンセプトを作るためのIDを作成します。


答えを編集して、具体化できますか?これはここのコメントのようです。ありがとう。
グレイ

0

@David Schmittsの回答に追加するには、経験則として、私は常にデフォルトの1Lを使用します。戻っていくつかの変更を数回行うだけで済みましたが、変更を加えてデフォルトの番号を毎回1つずつ更新すると、そのことがわかっていました。

私の現在の会社では自動生成された番号が必要なので、慣例としてそれを使用しますが、デフォルトを使用します。何らかの理由でシリアル化されたクラスの構造を絶えず変更することになると思わない限り、私の慣習として、それが作業する慣習ではない場合は、デフォルトを使用します。


0

シリアル化バージョンUIDの目的は、オブジェクトの有効なシリアル化を実行するために、クラスのさまざまなバージョンを追跡することです。

アイデアは、クラスの特定のバージョンに固有のIDを生成することです。このIDは、シリアル化されたオブジェクトの構造に影響を与える新しいフィールドなど、クラスに新しい詳細が追加されたときに変更されます。

簡単な説明:

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

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

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

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

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

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

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

ここで、serialVersionUIDについて心配する必要があり、それを詳しく調べる必要があります。

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

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