Warum eine lange serialVersionUID anstelle einer einfachen 1L generieren?

210

Wenn die Klasse Serializable in Eclipse implementiert, habe ich zwei Optionen: Standard hinzufügen serialVersionUID(1L)oder generiert serialVersionUID(3567653491060394677L). Ich denke, dass der erste cooler ist, aber oft habe ich Leute gesehen, die die zweite Option benutzt haben. Gibt es einen Grund zu generieren long serialVersionUID?

IAdapter
quelle
49
Wie ist das genaue Duplikat? Ich frage nicht, warum es überhaupt generiert wird, sondern warum lange serialVersionUID generiert wird.
IAdapter
13
Wenn Jon Skeet serialVersionUID verwendet, verwendet er 0L: stackoverflow.com/questions/605828/… ;)
Hanno Fietz
8
@HannoFietz: Der genaue Satz lautet: "Der Einfachheit halber würde ich vorschlagen , mit 0 zu beginnen und ihn jedes Mal um 1 zu erhöhen ." Es hört sich also so an, als würde er 0Lnur anfänglich verwenden.
ODER Mapper
7
@ORMapper: Wollen Sie damit sagen, dass Jon Skeet jemals zurückgehen und den von ihm geschriebenen Code aktualisieren muss? Sogar bis zur strukturellen Inkompatibilität. Keuchen! Ketzerei!
Thilo

Antworten:

90

Soweit ich das beurteilen kann, wäre dies nur aus Gründen der Kompatibilität mit früheren Versionen möglich. Dies ist nur dann nützlich, wenn Sie zuvor die Verwendung einer serialVersionUID versäumt haben und dann eine Änderung vorgenommen haben, von der Sie wissen, dass sie kompatibel sein sollte, die jedoch dazu führt, dass die Serialisierung unterbrochen wird .

Weitere Informationen finden Sie in der Java-Serialisierungsspezifikation .

Michael Myers
quelle
69

Der Zweck der UID der Serialisierungsversion besteht darin, verschiedene Versionen einer Klasse zu verfolgen, um eine gültige Serialisierung von Objekten durchzuführen.

Die Idee ist, eine ID zu generieren, die für eine bestimmte Version einer Klasse eindeutig ist. Diese wird dann geändert, wenn der Klasse neue Details hinzugefügt werden, z. B. ein neues Feld, das sich auf die Struktur des serialisierten Objekts auswirkt.

1LWenn Sie immer dieselbe ID verwenden, z. B. wenn in Zukunft die Klassendefinition geändert wird, was zu Änderungen an der Struktur des serialisierten Objekts führt, besteht eine gute Wahrscheinlichkeit, dass beim Versuch, ein Objekt zu deserialisieren, Probleme auftreten.

Wenn die ID weggelassen wird, berechnet Java die ID für Sie basierend auf den Feldern des Objekts. Ich glaube jedoch, dass dies ein teurer Prozess ist. Wenn Sie also eine ID manuell bereitstellen, wird die Leistung verbessert.

Hier sind einige Links zu Artikeln, die die Serialisierung und Versionierung von Klassen behandeln:

Coobird
quelle
50
Die Idee hinter der Verwendung von 1L besteht darin, dass Sie es jedes Mal erhöhen, wenn Sie die Klasseneigenschaften oder -methoden ändern.
Powerlord
39
Es gibt keine Auswirkungen auf die Laufzeitleistung, wenn die serialversionUID automatisch generiert wird - sie wird zur Kompilierungszeit von javac generiert. Wenn Sie den Bytecode der Klasse dekompilieren, wird die Variable tatsächlich statisch im Bytecode angezeigt.
Jared
11
Noch ein Hinweis: Wenn Sie die Nummer explizit verwalten, können Sie entscheiden, wann Sie Versionen einer Klasse als "kompatibel" betrachten, anstatt dass die Klassendefinition genau gleich sein muss.
Scott Stanchfield
23
@ Jared Gemäß Punkt 75 in Josh Blochs Effective Java: 2nd Edition: "Deklarieren Sie in jeder serialisierbaren Klasse, die Sie schreiben, eine explizite serielle Versions-UID. Wenn keine serielle Versions-UID angegeben ist, ist eine teure Berechnung erforderlich, um zur Laufzeit eine zu generieren . "
Colin K
12
@coobird Dies scheint der Hauptgrund zu sein, warum die standardmäßige serialVersionUID nicht empfohlen wird. 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. Der obige Kommentar stammt aus der Java Object Serialization Specification Version 6.0
user624558
20

Der Hauptgrund für die generierte Version besteht darin, sie mit einer vorhandenen Version der Klasse kompatibel zu machen, die bereits Kopien beibehalten hat.

Robin
quelle
1
OK, aber das gleiche wird sein, wenn ich immer 1L habe. Alles wird kompatibel sein, auch wenn ich Änderungen vornehmen werde.
grep
@grep versuche ein Feld umzubenennen und schau dann, was passiert.
Trejkaz
1
@grep der Punkt ist, dass wenn Sie eine Klasse haben, die die serialVersionUID zuvor weggelassen hat, sie die generierte automatisch erhalten hätte. Wenn Sie es jetzt explizit festlegen möchten, wird es durch Festlegen auf 1L nicht mit der vorhandenen Klasse kompatibel, während die Verwendung des generierten Werts die Kompatibilität beibehält.
März 82ch
15

Der "lange" Standardwert von serialVersionUIDist der Standardwert, der in der Java-Serialisierungsspezifikation definiert ist und aus dem Standard-Serialisierungsverhalten berechnet wird.

Wenn Sie also die Standardversionsnummer hinzufügen, wird Ihre Klasse schneller (de-) serialisiert, solange sich nichts strukturell geändert hat. Sie müssen jedoch darauf achten, dass Sie beim Ändern der Klasse (Hinzufügen / Entfernen von Feldern) auch die aktualisieren Ordnungsnummer.

Wenn Sie nicht mit vorhandenen Bitströmen kompatibel sein müssen, können Sie sie einfach 1Ldort ablegen und die Version nach Bedarf erhöhen, wenn sich etwas ändert. Das heißt, wenn sich die Standardversion der Serialisierung der geänderten Klasse von der Standardversion der alten Klasse unterscheidet.

David Schmitt
quelle
11

Sie sollten unbedingt jedes Mal eine serialVersionUID erstellen, wenn Sie eine Klasse definieren, die implementiert wird java.io.Serializable. Wenn Sie dies nicht tun, wird automatisch eine für Sie erstellt, aber das ist schlecht. Die automatisch generierte serialVersionUID basiert auf den Methodensignaturen Ihrer Klasse. Wenn Sie also Ihre Klasse in Zukunft ändern, um beispielsweise eine Methode hinzuzufügen, schlägt die Deserialisierung der "alten" Versionen der Klasse fehl. Folgendes kann passieren:

  1. Erstellen Sie die erste Version Ihrer Klasse, ohne die serialVersionUID zu definieren.
  2. Serialisieren Sie eine Instanz Ihrer Klasse in einen dauerhaften Speicher. Eine serialVersionUID wird automatisch für Sie generiert.
  3. Ändern Sie Ihre Klasse, um eine neue Methode hinzuzufügen, und stellen Sie Ihre Anwendung erneut bereit.
  4. Versuchen Sie, die in Schritt 2 serialisierte Instanz zu deserialisieren, aber jetzt schlägt sie fehl (wenn sie erfolgreich sein sollte), da sie eine andere automatisch generierte serialVersionUID hat.
Pankaj Kumar
quelle
1
In der Tat sollte das Deserialisieren alter Versionen der Klasse tatsächlich fehlschlagen, da sie nicht mehr gleich sind. Sie schlagen vor, die serialVersionUID selbst zu generieren, um (De-) Serialisierungsfehler zu vermeiden, wenn sich die Klassensignatur ändert. Obwohl Ihr Vorschlag angemessen ist, ist Ihre Erklärung des Zwecks einfach falsch und irreführend. Es wäre ratsam, Ihre Antwort zu ändern.
Mostruash
6

Wenn Sie keine serialVersionUID angeben, erstellt Java eine im laufenden Betrieb. Die generierte serialVersionUID ist diese Nummer. Wenn Sie etwas in Ihrer Klasse ändern, das Ihre Klasse nicht wirklich mit früheren serialisierten Versionen inkompatibel macht, sondern den Hash ändert, müssen Sie die generierte serialVersionUID mit sehr großer Nummer (oder die "erwartete" Nummer aus der Fehlermeldung) verwenden. . Andernfalls ist 0, 1, 2 ... besser, wenn Sie alles selbst im Auge behalten.

Joeforker
quelle
Sie meinten ==> 1. Wenn Sie möchten, dass verschiedene Klassenänderungen kompatibel sind, verwenden Sie die generierte. 2. Wenn Sie möchten, dass verschiedene Versionen von Klassen nicht kompatibel sind, verwenden Sie die Standardversion und gehen Sie beim Inkrementieren umsichtig vor. Habe ich es richtig verstanden
JavaDeveloper
4

Wenn Sie serialVersionUID (1L) verwenden, anstatt serialVersionUID (3567653491060394677L) zu generieren, sagen Sie etwas.

Sie sagen, dass Sie zu 100% sicher sind, dass kein System, das diese Klasse jemals berühren wird, eine inkompatible serialisierte Version dieser Klasse mit der Versionsnummer 1 hat.

Wenn Sie sich eine Entschuldigung dafür vorstellen können, dass der Verlauf der serialisierten Version unbekannt ist, ist dies möglicherweise schwer mit Sicherheit zu sagen. Zu Lebzeiten wird eine erfolgreiche Klasse von vielen Menschen gepflegt, in vielen Projekten gelebt und in vielen Systemen untergebracht.

Darüber können Sie sich quälen. Oder Sie können im Lotto spielen und hoffen zu verlieren. Wenn Sie die Version generieren, besteht eine geringe Wahrscheinlichkeit, dass etwas schief geht. Wenn Sie davon ausgehen, dass "Hey, ich wette, noch hat niemand 1 verwendet", sind Ihre Chancen größer als winzig. Gerade weil wir alle 0 und 1 für cool halten, haben Sie höhere Chancen, sie zu treffen.

- -

Wenn Sie serialVersionUID (3567653491060394677L) generieren, anstatt serialVersionUID (1L) zu verwenden, sagen Sie etwas.

Sie sagen, dass die Leute im Laufe der Geschichte dieser Klasse möglicherweise andere Versionsnummern manuell erstellt oder generiert haben, und es ist Ihnen egal, weil Longs große Zahlen ausflippen.

In beiden Fällen gehen Sie ein Risiko ein, es sei denn, Sie kennen den Verlauf der Versionsnummern, die bei der Serialisierung der Klasse im gesamten Universum, in dem sie existiert oder jemals existieren wird, verwendet werden, genau. Wenn Sie die Zeit haben, 100% sicher zu stellen, dass 1 AOK ist, machen Sie es. Wenn das zu viel Arbeit ist, generieren Sie die Nummer blind. Es ist wahrscheinlicher, dass Sie im Lotto gewinnen, als dass dies schief geht. Wenn ja, lass es mich wissen und ich kaufe dir ein Bier.

Bei all dem Gerede über das Lotto spielen habe ich vielleicht den Eindruck erweckt, dass serialVersionUID zufällig generiert wird. In der Tat, solange der Zahlenbereich gleichmäßig über jeden möglichen Wert eines Long verteilt ist, wäre das in Ordnung. Es wird jedoch tatsächlich so gemacht:

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

Der einzige Unterschied, den Sie dabei bekommen, ist, dass Sie keine Zufallsquelle benötigen. Sie verwenden die Änderungen in der Klasse selbst, um das Ergebnis zu ändern. Nach dem Pigeonhole-Prinzip besteht jedoch immer noch die Möglichkeit, dass ein Fehler auftritt und eine Kollision auftritt. Es ist einfach unglaublich unwahrscheinlich. Also viel Glück, ein Bier aus mir herauszuholen.

Selbst wenn die Klasse immer nur in einem System und einer Codebasis leben wird, bedeutet der Gedanke, dass das Erhöhen der Zahl von Hand keine Kollisionswahrscheinlichkeit bietet, nur, dass Sie Menschen nicht verstehen. :) :)

Irgendein Typ
quelle
Wenn ein "System" die Klasse berührt, dh die Klasse so ändert, dass die Serialisierung nicht mehr kompatibel ist, stellt sich die Frage, ob dieses System auch die serialVersionUID ändert. Ich denke nicht, dass die Chancen geringer sind, dass es daran denken wird, es zu ändern, wenn es lang ist. Ich denke eher das Gegenteil ist der Fall, wenn die Anzahl leichter zu merken ist, sind die Änderungen höher, als ich bemerke, dass ich sie versehentlich nicht geändert habe.
Reto Gmür
2
Das ist falsch! Wenn Sie serialVersionUID generieren und diesen Wert in Ihrem Quellcode deklarieren, anstatt 1L oder nichts, sagen Sie tatsächlich: Ich möchte in Zukunft eine möglicherweise unentdeckte Kollision mit undefinierten Effekten, und ich möchte nicht, dass Java oder andere Menschen dies verhindern . Java ist paranoid, aber gehorsam. Menschen spielen normalerweise nicht mit großen Zahlen. Auf diese Weise kann Java bei Änderungen der Klasse immer noch alte inkompatible Versionen de-serialisieren. MwoaHaHa ...;)
Superole
1

Nun, serialVersionUID ist eine Ausnahme von der Regel, dass "statische Felder nicht serialisiert werden". ObjectOutputStream schreibt jedes Mal den Wert von serialVersionUID in den Ausgabestream. ObjectInputStream liest es zurück und wenn der aus dem Stream gelesene Wert nicht mit dem Wert serialVersionUID in der aktuellen Version der Klasse übereinstimmt, wird die InvalidClassException ausgelöst. Wenn in der zu serialisierenden Klasse keine serialVersionUID offiziell deklariert ist, fügt der Compiler diese automatisch mit einem Wert hinzu, der basierend auf den in der Klasse deklarierten Feldern generiert wird.

Glücklich
quelle
0

Weil in vielen Fällen die Standard-ID nicht eindeutig ist. Deshalb erstellen wir eine ID, um ein einzigartiges Konzept zu erstellen.

Pushpendra Kuntal
quelle
Können Sie Ihre Antwort bearbeiten, um sie genauer zu formulieren? Dies scheint hier ein Kommentar zu sein. Vielen Dank.
Grau
0

Um die Antwort von @David Schmitts zu ergänzen, würde ich als Faustregel immer die Standard-1L außerhalb der Konvention verwenden. Ich musste nur ein paar Mal zurückgehen und einige davon ändern, aber ich wusste das, als ich die Änderung vornahm und die Standardnummer jedes Mal um eins aktualisierte.

In meiner aktuellen Firma benötigen sie die automatisch generierte Nummer, daher verwende ich diese für die Konvention, aber ich bevorzuge die Standardeinstellung. Wenn es sich nicht um eine Konvention handelt, bei der Sie arbeiten, verwenden Sie die Standardeinstellung, es sei denn, Sie glauben, dass Sie aus irgendeinem Grund die Struktur Ihrer serialisierten Klassen ständig ändern werden.

James Drinkard
quelle
0

Der Zweck der UID der Serialisierungsversion besteht darin, verschiedene Versionen einer Klasse zu verfolgen, um eine gültige Serialisierung von Objekten durchzuführen.

Die Idee ist, eine ID zu generieren, die für eine bestimmte Version einer Klasse eindeutig ist. Diese wird dann geändert, wenn der Klasse neue Details hinzugefügt werden, z. B. ein neues Feld, das sich auf die Struktur des serialisierten Objekts auswirkt.

Eine einfache Erklärung:

Serialisieren Sie Daten?

Bei der Serialisierung werden im Grunde Klassendaten in eine Datei / einen Stream / etc. Geschrieben. Bei der De-Serialisierung werden diese Daten in eine Klasse zurückgelesen.

Beabsichtigen Sie, in Produktion zu gehen?

Wenn Sie nur etwas mit unwichtigen / gefälschten Daten testen, machen Sie sich darüber keine Sorgen (es sei denn, Sie testen die Serialisierung direkt).

Ist das die erste Version?

Wenn ja, setzen Sie serialVersionUID = 1L.

Ist dies die zweite, dritte usw. Produktversion?

Jetzt müssen Sie sich um serialVersionUID kümmern und sollten sich eingehend damit befassen.

Wenn Sie die Version beim Aktualisieren einer Klasse, die Sie schreiben / lesen müssen, nicht korrekt aktualisieren, wird im Grunde eine Fehlermeldung angezeigt, wenn Sie versuchen, alte Daten zu lesen.

Saurabh Verma
quelle