Pourquoi générer un serialVersionUID long au lieu d'un simple 1L?

210

Lorsque la classe implémente Serializable dans Eclipse, j'ai deux options: ajouter par défaut serialVersionUID(1L)ou généré serialVersionUID(3567653491060394677L). Je pense que la première est plus cool, mais plusieurs fois j'ai vu des gens utiliser la deuxième option. Y a-t-il une raison de générer long serialVersionUID?

IAdapter
la source
49
Comment est ce duplicata exact? Je ne demande pas pourquoi le générer, mais pourquoi générer un serialVersionUID long.
IAdapter
13
Lorsque Jon Skeet utilise serialVersionUID, il utilise 0L: stackoverflow.com/questions/605828/… ;)
Hanno Fietz
8
@HannoFietz: La phrase exacte est: "Pour plus de simplicité, je suggère de commencer par 0 et de l' augmenter de 1 chaque fois que vous en avez besoin." Donc, il semble qu'il n'utilise 0Lqu'au début.
OR Mapper
7
@ORMapper: Voulez-vous dire que Jon Skeet a besoin de revenir en arrière et de mettre à jour le code qu'il a écrit? Même au point d'incompatibilité structurelle. Haleter! Hérésie!
Thilo

Réponses:

90

Pour autant que je sache, ce serait uniquement pour des raisons de compatibilité avec les versions précédentes. Cela ne serait utile que si vous avez négligé d'utiliser un serialVersionUID auparavant, puis effectué une modification que vous savez être compatible, mais qui provoque la rupture de la sérialisation.

Voir la spécification de sérialisation Java pour plus de détails.

Michael Myers
la source
69

Le but de la version de sérialisation UID est de garder une trace des différentes versions d'une classe afin d'effectuer une sérialisation valide des objets.

L'idée est de générer un ID unique à une certaine version d'une classe, qui est ensuite modifié lorsque de nouveaux détails sont ajoutés à la classe, comme un nouveau champ, ce qui affecterait la structure de l'objet sérialisé.

Toujours en utilisant le même ID, ce qui 1Lsignifie qu'à l'avenir, si la définition de classe est modifiée, ce qui entraîne des modifications de la structure de l'objet sérialisé, il y a de fortes chances que des problèmes surviennent lors de la tentative de désérialisation d'un objet.

Si l'ID est omis, Java calculera réellement l'ID pour vous en fonction des champs de l'objet, mais je pense que c'est un processus coûteux, donc en fournir un manuellement améliorera les performances.

Voici quelques liens vers des articles qui traitent de la sérialisation et du versionnement des classes:

coobird
la source
50
L'idée derrière l'utilisation de 1L est de l'incrémenter à chaque fois que vous modifiez les propriétés ou les méthodes de classe.
Powerlord
39
Il n'y a pas d'impact sur les performances d'exécution de permettre à serialversionUID d'être généré automatiquement - il est généré au moment de la compilation par javac ... si vous décompilez le bytecode de la classe, vous verrez en fait la variable statiquement dans le bytecode.
Jared
11
Encore une remarque - en gérant le nombre de manière explicite, vous décidez quand vous considérez les versions d'une classe "compatibles", plutôt que d'exiger que la définition de classe soit exactement la même.
Scott Stanchfield
23
@ Jared selon l'article 75 de Josh Bloch's Effective Java: 2nd Edition: "déclarez un UID de version série explicite dans chaque classe sérialisable que vous écrivez .... Si aucun UID de version série n'est fourni, un calcul coûteux est nécessaire pour en générer un au moment de l'exécution . "
Colin K
12
@coobird Cela semble être la principale raison pour laquelle le serialVersionUID par défaut n'est pas recommandé 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. Le commentaire ci-dessus a été tiré de la spécification Java Object Serialization Specification version 6.0
user624558
20

La principale raison de la génération serait de la rendre compatible avec une version existante de la classe qui a déjà des copies persistantes.

Robin
la source
1
OK mais ce sera le cas si j'ai toujours 1L. Tout sera compatible même si j'apporte des modifications.
grep
@grep essayez de renommer un champ et voyez ce qui se passe.
Trejkaz
1
@grep, le fait est que si vous aviez une classe qui avait omis le serialVersionUID auparavant, elle aurait obtenu celle générée automatiquement. Alors maintenant, vous voulez commencer à le définir explicitement, le définir sur 1L le rendrait incompatible avec la classe existante, tandis que l'utilisation de la valeur générée le maintient compatible.
marc82ch
15

La valeur par défaut "longue" de serialVersionUIDest la valeur par défaut définie par la spécification de sérialisation Java , calculée à partir du comportement de sérialisation par défaut.

Donc, si vous ajoutez le numéro de version par défaut, votre classe (dé-) sérialise plus rapidement tant que rien n'a structurellement changé, mais vous devrez vous assurer que si vous changez la classe (ajouter / supprimer des champs), vous mettrez également à jour le numéro de série.

Si vous n'avez pas besoin d'être compatible avec les flux binaires existants, vous pouvez simplement 1Ly mettre et incrémenter la version selon les besoins lorsque quelque chose change. Autrement dit, lorsque la version de sérialisation par défaut de la classe modifiée serait différente de la version par défaut de l'ancienne classe.

David Schmitt
la source
11

Vous devez absolument créer un serialVersionUID chaque fois que vous définissez une classe qui implémente java.io.Serializable. Si vous ne le faites pas, un sera créé automatiquement pour vous, mais c'est mauvais. Le serialVersionUID généré automatiquement est basé sur les signatures de méthode de votre classe, donc si vous changez votre classe à l'avenir pour ajouter une méthode (par exemple), la désérialisation des "anciennes" versions de la classe échouera. Voici ce qui peut arriver:

  1. Créez la première version de votre classe, sans définir le serialVersionUID.
  2. Sérialiser une instance de votre classe dans un magasin persistant; un serialVersionUID est automatiquement généré pour vous.
  3. Modifiez votre classe pour ajouter une nouvelle méthode et redéployez votre application.
  4. Essayez de désérialiser l'instance qui a été sérialisée à l'étape 2, mais maintenant elle échoue (quand elle devrait réussir), car elle a un serialVersionUID généré automatiquement différent.
Pankaj Kumar
la source
1
En fait, la désérialisation des anciennes versions de la classe devrait en effet échouer car elles ne sont plus les mêmes. Vous suggérez de générer serialVersionUID par vous-même pour éviter les échecs de (dé) sérialisation lorsque la signature de classe change. Bien que votre suggestion soit appropriée, votre explication de son objectif est tout simplement erronée et trompeuse. Il serait sage de modifier votre réponse.
mostruash
6

Si vous ne spécifiez pas un serialVersionUID, Java en crée un à la volée. Le serialVersionUID généré est ce numéro. Si vous changez quelque chose dans votre classe qui ne rend pas vraiment votre classe incompatible avec les versions précédentes sérialisées mais modifie le hachage, alors vous devez utiliser le serialVersionUID généré en très grand nombre (ou le numéro "attendu" du message d'erreur) . Sinon, si vous suivez tout vous-même, 0, 1, 2 ... c'est mieux.

joeforker
la source
Vous vouliez dire ==> 1. Si vous voulez que différents changements de classes soient compatibles, utilisez celui généré. 2. Si vous souhaitez que différentes versions de classes soient incompatibles, utilisez celle par défaut et soyez prudent dans l'incrémentation. L'ai-je bien compris?
JavaDeveloper
4

Lorsque vous utilisez serialVersionUID (1L) plutôt que de générer serialVersionUID (3567653491060394677L), vous dites quelque chose.

Vous dites que vous êtes sûr à 100% qu'aucun système ne touchera jamais cette classe qui a une version sérialisée incompatible de cette classe avec un numéro de version de 1.

Si vous pouvez penser à une excuse pour que l'historique de sa version sérialisée soit inconnu, cela pourrait être difficile à dire avec confiance. Au cours de sa vie, un cours réussi sera maintenu par de nombreuses personnes, vivra dans de nombreux projets et résidera dans de nombreux systèmes.

Vous pouvez agoniser à ce sujet. Ou vous pouvez jouer à la loterie en espérant perdre. Si vous générez la version, vous avez une petite chance que les choses tournent mal. Si vous supposez "Hé, je parie que personne n'en a encore utilisé 1", vos chances sont plus grandes que minuscules. C'est précisément parce que nous pensons tous que 0 et 1 sont cool que vous avez plus de chances de les toucher.

-

Lorsque vous générez serialVersionUID (3567653491060394677L) plutôt que d'utiliser serialVersionUID (1L), vous dites quelque chose.

Vous dites que les gens peuvent avoir créé ou généré manuellement d'autres numéros de version au cours de l'histoire de cette classe et cela ne vous dérange pas parce que les Longs font flipper de gros nombres.

Quoi qu'il en soit, à moins que vous ne connaissiez parfaitement l'historique des numéros de version utilisés lors de la sérialisation de la classe dans l'univers entier de l'endroit où elle a existé ou existera, vous tentez. Si vous avez le temps de vous assurer à 100% que 1 est AOK, allez-y. Si c'est trop de travail, allez-y et générez aveuglément le nombre. Vous avez plus de chances de gagner à la loterie que de vous tromper. Si c'est le cas, faites-le moi savoir et je vous achèterai une bière.

Avec toutes ces discussions sur la loterie, je vous ai peut-être donné l'impression que serialVersionUID est généré de manière aléatoire. En fait, tant que la plage de nombres est répartie uniformément sur toutes les valeurs possibles d'un Long, ce serait bien. Cependant, cela se fait de cette façon:

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

La seule différence que vous obtenez avec cela est que vous n'avez pas besoin d'une source de hasard. Vous utilisez les changements dans la classe elle-même pour changer le résultat. Mais selon le principe du pigeonhole, il y a encore une chance qu'il puisse mal tourner et avoir une collision. C'est incroyablement improbable. Alors bonne chance pour avoir une bière de moi.

Cependant, même si la classe ne vivra que dans un seul système et une seule base de code, penser qu'incrémenter le nombre à la main vous donne zéro chance de collision signifie simplement que vous ne comprenez pas les humains. :)

Un gars
la source
Si un "système" touche la classe, c'est-à-dire qu'il change la classe de manière à ce que la sérialisation devienne incompatible, la question est de savoir si ce système changera également le serialVersionUID. Je ne pense pas que les chances soient plus faibles qu'il se souviendra de le changer quand il sera long. Je pense plutôt que le contraire est vrai, si le nombre est plus facile à retenir, les changements sont plus élevés que je remarque que je ne l'ai pas accidentellement changé.
Reto Gmür
2
C'est faux! Lorsque vous générez serialVersionUID et déclarez cette valeur dans votre code source, plutôt que 1L ou rien, vous dites en fait: je veux une collision éventuellement non détectée à l'avenir avec des effets indéfinis, et je ne veux pas que Java ou des humains empêchent cela . Java est paranoïaque, mais obéissant. Les humains ne plaisantent généralement pas avec les grands nombres. De cette façon, lorsque la classe change, java peut toujours dé-sérialiser ses anciennes versions incompatibles. MwoaHaHa ...;)
Superole
1

Eh bien, serialVersionUID est une exception à la règle selon laquelle «les champs statiques ne sont pas sérialisés». ObjectOutputStream écrit chaque fois la valeur de serialVersionUID dans le flux de sortie. ObjectInputStream le relit et si la valeur lue dans le flux ne correspond pas à la valeur serialVersionUID dans la version actuelle de la classe, il lève l'exception InvalidClassException. De plus, s'il n'y a pas de serialVersionUID officiellement déclaré dans la classe à sérialiser, le compilateur l'ajoute automatiquement avec une valeur générée en fonction des champs déclarés dans la classe.

Chanceux
la source
0

Parce que dans de nombreux cas, l'ID par défaut n'est pas unique. nous créons donc id pour faire un concept unique.

Pushpendra Kuntal
la source
Pouvez-vous modifier votre réponse pour l'étoffer davantage? Cela semble être un commentaire ici. Merci.
Gray
0

Pour ajouter à la réponse de @David Schmitts, en règle générale, j'utiliserais toujours le 1 L par défaut par convention. Je n'ai eu qu'à revenir en arrière et à changer certains d'entre eux quelques fois, mais je le savais quand j'ai fait le changement et mis à jour le numéro par défaut d'un à chaque fois.

Dans ma société actuelle, ils ont besoin du numéro généré automatiquement, donc je l'utilise pour la convention, mais je préfère la valeur par défaut. Mon avis est, si ce n'est pas une convention où vous travaillez, utilisez la valeur par défaut, à moins que vous pensiez que vous changerez constamment la structure de vos classes sérialisées pour une raison quelconque.

James Drinkard
la source
0

Le but de la version de sérialisation UID est de garder une trace des différentes versions d'une classe afin d'effectuer une sérialisation valide des objets.

L'idée est de générer un ID unique à une certaine version d'une classe, qui est ensuite modifié lorsque de nouveaux détails sont ajoutés à la classe, comme un nouveau champ, ce qui affecterait la structure de l'objet sérialisé.

Une explication simple:

Êtes-vous en train de sérialiser des données?

La sérialisation consiste essentiellement à écrire des données de classe dans un fichier / flux / etc. La désérialisation consiste à lire ces données dans une classe.

Comptez-vous entrer en production?

Si vous testez simplement quelque chose avec des données non importantes / fausses, ne vous en faites pas (sauf si vous testez directement la sérialisation).

Est-ce la première version?

Si tel est le cas, définissez serialVersionUID = 1L.

S'agit-il de la deuxième, troisième, etc. version prod?

Maintenant, vous devez vous soucier de serialVersionUID et vous devez l'examiner en profondeur.

Fondamentalement, si vous ne mettez pas à jour la version correctement lorsque vous mettez à jour une classe que vous devez écrire / lire, vous obtiendrez une erreur lorsque vous essayez de lire les anciennes données.

Saurabh Verma
la source