Question Qu'est-ce qu'un serialVersionUID et pourquoi devrais-je l'utiliser?


Eclipse émet des avertissements lorsqu'un serialVersionUID est manquant.

La classe sérialisable Foo ne déclare pas une finale statique   champ serialVersionUID de type long

Quel est serialVersionUID Et pourquoi est-ce important? S'il vous plaît montrer un exemple où manquant serialVersionUID causera un problème.


2482
2017-11-12 23:24


origine


Réponses:


Les documents pour java.io.Serializable sont probablement à peu près aussi bien une explication que vous obtiendrez:

Les associés à l'exécution de sérialisation   avec chaque classe sérialisable une version   number, appelé serialVersionUID,   qui est utilisé pendant la désérialisation   vérifier que l'expéditeur et le destinataire   d'un objet sérialisé ont chargé   classes pour cet objet qui sont   compatible avec   sérialisation. Si le récepteur a   chargé une classe pour l'objet qui a   un serialVersionUID différent de celui   de la classe de l'expéditeur correspondant,   puis la désérialisation entraînera une    InvalidClassException. Une sérialisable   classe peut déclarer son propre   serialVersionUID explicitement par   déclarer un champ nommé   "serialVersionUID" ça doit être   statique, finale et de type long:

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

Si un   classe sérialisable ne pas explicitement   déclare un serialVersionUID, puis le   l'exécution de sérialisation va calculer un   valeur serialVersionUID par défaut pour   cette classe basée sur divers aspects de   la classe, comme décrit dans le   Sérialisation d'objet Java (TM)   Spécification. Cependant, il est fortement   conseillé que tout sérialisable   les classes déclarent explicitement   valeurs serialVersionUID, puisque   calcul serialVersionUID par défaut   est très sensible aux détails de la classe   cela peut varier en fonction du compilateur   implémentations, et peut donc entraîner   en inattendu InvalidClassExceptions   pendant la désérialisation. Par conséquent, pour   garantir une cohérence   valeur serialVersionUID sur   compilateur Java différent   implémentations, une classe sérialisable   doit déclarer explicitement   valeur serialVersionUID. C'est aussi   fortement conseillé que explicite   Les déclarations serialVersionUID utilisent le   modificateur privé si possible, depuis   ces déclarations ne s'appliquent qu'au   déclarer immédiatement   classe - les champs serialVersionUID ne sont pas   utile en tant que membres hérités.


1910
2017-11-12 23:30



Si vous êtes en sérialisation juste parce que vous devez sérialiser pour l'implémentation (qui se soucie si vous sérialiser pour une HTTPSession, par exemple ... si elle est stockée ou non, vous ne vous souciez probablement pas de désérialiser un objet de formulaire) , alors vous pouvez ignorer cela.

Si vous utilisez réellement la sérialisation, cela n'a d'importance que si vous envisagez de stocker et de récupérer des objets directement à l'aide de la sérialisation. Le serialVersionUID représente votre version de classe, et vous devez l'incrémenter si la version actuelle de votre classe n'est pas rétrocompatible avec sa version précédente.

La plupart du temps, vous n'utiliserez probablement pas la sérialisation directement. Si c'est le cas, générez un UID sérialisable par défaut en cliquant sur l'option de correction rapide et ne vous inquiétez pas.


415
2017-11-13 04:24



Je ne peux pas laisser passer cette occasion de brancher le livre de Josh Bloch Java efficace (2ème édition). Le chapitre 11 est une ressource indispensable sur la sérialisation Java.

Par Josh, l'UID généré automatiquement est généré en fonction d'un nom de classe, d'interfaces implémentées et de tous les membres publics et protégés. Changer n'importe lequel d'entre eux changera le serialVersionUID. Vous n'avez donc pas besoin de les manipuler uniquement si vous êtes certain que pas plus d'une version de la classe ne sera jamais sérialisée (soit à travers les processus, soit récupérée à partir du stockage à un moment ultérieur).

Si vous les ignorez pour le moment et que vous trouvez plus tard que vous devez modifier la classe d'une manière ou d'une autre mais que vous maintenez la compatibilité avec l'ancienne version de la classe, vous pouvez utiliser l'outil JDK serialver pour générer le serialVersionUID sur le vieux class, et définir explicitement cela sur la nouvelle classe. (En fonction de vos modifications, vous devrez peut-être également implémenter la sérialisation personnalisée en ajoutant writeObject et readObject méthodes - voir Serializable javadoc ou chapitre 11 précité).


264
2017-11-12 23:37



Vous pouvez demander à Eclipse d'ignorer ces avertissements serialVersionUID:

Fenêtre> Préférences> Java> Compilateur> Erreurs / avertissements> Problèmes de programmation potentiels

Au cas où vous ne le sauriez pas, il y a beaucoup d'autres avertissements que vous pouvez activer dans cette section (ou même dont certains sont signalés comme des erreurs), beaucoup sont très utiles:

  • Problèmes potentiels de programmation: Affectation booléenne accidentelle possible
  • Problèmes de programmation potentiels: Accès au pointeur nul
  • Code inutile: La variable locale n'est jamais lue
  • Code inutile: vérification null redondante
  • Code inutile: Cast inutile ou 'instanceof'

et beaucoup plus.


124
2017-11-13 01:17



serialVersionUID facilite le versionnement des données sérialisées. Sa valeur est stockée avec les données lors de la sérialisation. Lors de la sérialisation, la même version est vérifiée pour voir comment les données sérialisées correspondent au code actuel.

Si vous voulez mettre à jour vos données, vous commencez normalement avec un serialVersionUID de 0, et le bump avec chaque changement structurel à votre classe qui modifie les données sérialisées (en ajoutant ou en supprimant les champs non-transitoires).

Le mécanisme de dé-sérialisation intégré (in.defaultReadObject()) refusera de sérialiser les anciennes versions des données. Mais si vous voulez vous pouvez définir votre propre readObject ()-fonction qui peut lire les anciennes données. Ce code personnalisé peut ensuite vérifier la serialVersionUID afin de savoir dans quelle version se trouvent les données et décider comment les dé-sérialiser. Cette technique de gestion des versions est utile si vous stockez des données sérialisées qui survivent à plusieurs versions de votre code.

Mais stocker des données sérialisées pour une période si longue n'est pas très commun. Il est beaucoup plus courant d'utiliser le mécanisme de sérialisation pour écrire temporairement des données dans un cache, par exemple, ou l'envoyer sur le réseau à un autre programme avec la même version des parties pertinentes de la base de code.

Dans ce cas, vous ne souhaitez pas conserver la compatibilité ascendante. Vous ne faites que vous assurer que les bases de code qui communiquent ont bien les mêmes versions de classes pertinentes. Afin de faciliter un tel contrôle, vous devez maintenir le serialVersionUIDjuste comme avant et ne pas oublier de le mettre à jour lorsque vous apportez des modifications à vos classes.

Si vous oubliez de mettre à jour le champ, vous pourriez vous retrouver avec deux versions différentes d'une classe avec une structure différente mais avec la même serialVersionUID. Si cela se produit, le mécanisme par défaut (in.defaultReadObject()) ne détectera aucune différence et tentera de dé-sérialiser les données incompatibles. Maintenant vous pourriez vous retrouver avec une erreur d'exécution cryptique ou une panne silencieuse (champs null). Ces types d'erreurs peuvent être difficiles à trouver.

Donc, pour faciliter cette utilisation, la plate-forme Java vous offre le choix de ne pas définir serialVersionUID manuellement. Au lieu de cela, un hachage de la structure de classe sera généré à la compilation et utilisé comme identifiant. Ce mécanisme s'assurera que vous n'avez jamais des structures de classe différentes avec le même identifiant, et que vous n'obtiendrez pas ces échecs de sérialisation d'exécution difficiles à tracer mentionnés ci-dessus.

Mais il y a un revers à la stratégie d'identification automatique générée. A savoir que les identifiants générés pour la même classe peuvent différer entre les compilateurs (comme mentionné par Jon Skeet ci-dessus). Donc, si vous communiquez des données sérialisées entre un code compilé avec différents compilateurs, il est recommandé de conserver les identifiants manuellement de toute façon.

Et si vous êtes rétrocompatible avec vos données comme dans le premier cas d'utilisation mentionné, vous voudrez probablement conserver l'identifiant vous-même. Ceci afin d'obtenir des identifiants lisibles et avoir un meilleur contrôle sur quand et comment ils changent.


96
2017-10-03 06:05



Qu'est-ce qu'un serialVersionUID et pourquoi devrais-je l'utiliser?

SerialVersionUID est un identifiant unique pour chaque classe, JVM l'utilise pour comparer les versions de la classe en s'assurant que la même classe a été utilisée pendant le chargement de la sérialisation pendant la désérialisation.

En spécifier un donne plus de contrôle, bien que JVM en génère un si vous ne le spécifiez pas. La valeur générée peut différer entre différents compilateurs. De plus, vous voulez parfois, pour une raison quelconque, interdire la désérialisation des anciens objets sérialisés [backward incompatibility], et dans ce cas vous devez juste changer le serialVersionUID.

le javadocs pour Serializable dire:

le calcul serialVersionUID par défaut est très sensible à la classe   les détails qui peuvent varier en fonction des implémentations du compilateur, et peuvent   donc entraîner inattendu InvalidClassExceptions pendant   désérialisation.

Par conséquent, vous devez déclarer serialVersionUID car cela nous donne plus de contrôle.

Cet article a quelques bons points sur le sujet.


62
2017-10-17 04:28



La question initiale a demandé "pourquoi est-ce important" et "exemple" où cette Serial Version ID serait utile. Eh bien, j'en ai trouvé un.

Dites que vous créez un Car class, instanciez-le et écrivez-le dans un flux d'objets. L'objet voiture aplati se trouve dans le système de fichiers pendant un certain temps. En attendant, si le Car La classe est modifiée en ajoutant un nouveau champ. Plus tard, lorsque vous essayez de lire (c'est-à-dire de désérialiser) l'aplatissement Car objet, vous obtenez le java.io.InvalidClassException - parce que toutes les classes sérialisables reçoivent automatiquement un identifiant unique. Cette exception est levée lorsque l'identifiant de la classe n'est pas égal à l'identifiant de l'objet aplati. Si vous y pensez vraiment, l'exception est levée à cause de l'ajout du nouveau champ. Vous pouvez éviter que cette exception ne soit levée en contrôlant vous-même le versionnement en déclarant explicitement serialVersionUID. Il y a aussi un petit avantage de performance en déclarant explicitement votre serialVersionUID(parce que n'a pas besoin d'être calculé). Il est donc recommandé d'ajouter votre propre serialVersionUID à vos classes Serializable dès que vous les créez comme indiqué ci-dessous:

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

41
2018-04-07 10:43



Si vous recevez cet avertissement sur une classe, vous ne pensez jamais à la sérialisation et vous ne vous êtes pas déclaré implements Serializable, c'est souvent parce que vous avez hérité d'une superclasse, qui implémente Serializable. Souvent, il serait préférable de déléguer à un tel objet au lieu d'utiliser l'héritage.

Donc, au lieu de

public class MyExample extends ArrayList<String> {

    public MyExample() {
        super();
    }
    ...
}

faire

public class MyExample {
    private List<String> myList;

    public MyExample() {
         this.myList = new ArrayList<String>();
    }
    ...
}

et dans les méthodes pertinentes appel myList.foo() au lieu de this.foo() (ou super.foo()). (Cela ne convient pas dans tous les cas, mais encore assez souvent.)

Je vois souvent des gens étendre JFrame ou autre, quand ils ont vraiment besoin de déléguer à cela. (Cela facilite également l'auto-complétion dans un IDE, puisque JFrame a des centaines de méthodes, dont vous n'avez pas besoin lorsque vous voulez appeler vos personnalisées sur votre classe.)

Un cas où l'avertissement (ou serialVersionUID) est inévitable est lorsque vous étendez à partir de AbstractAction, normalement dans une classe anonyme, en ajoutant uniquement la méthode actionPerformed. Je pense qu'il ne devrait pas y avoir d'avertissement dans ce cas (puisque vous ne pouvez normalement pas sérialiser et désérialiser de telles classes anonymes de différentes versions de votre classe), mais je ne sais pas comment le compilateur pourrait le reconnaître.


33
2018-02-03 00:32