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




serialversionuid=1l (17)

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

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

Qu'est-ce que serialVersionUID et pourquoi est-ce important? Veuillez montrer un exemple où l'absence de serialVersionUID un problème.


La question initiale demandait "Pourquoi est-ce important" et "un exemple" où cet Serial Version ID serait utile. Eh bien j'en ai trouvé un.

Supposons que vous créez une classe Car , que vous l'instanciez et que vous l'écriviez dans un flux d'objets. L'objet voiture aplati reste dans le système de fichiers pendant un certain temps. En attendant, si la classe Car est modifiée en ajoutant un nouveau champ. Plus tard, lorsque vous essayez de lire (c’est-à-dire de désérialiser) l’objet Car aplati, vous obtenez l’ java.io.InvalidClassException car un identifiant unique est automatiquement attribué à toutes les classes sérialisables. Cette exception est levée lorsque l'identifiant de la classe n'est pas égal à l'identifiant de l'objet aplati. Si vous y réfléchissez vraiment, l'exception est levée à cause de l'ajout du nouveau champ. Vous pouvez éviter cette exception en contrôlant vous-même la gestion des versions en déclarant un serialVersionUID explicite. La déclaration explicite de votre serialVersionUID présente également un petit avantage en serialVersionUID (car elle ne doit pas être calculée). 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
}

Chaque fois qu'un objet est sérialisé, l'objet est marqué d'un numéro d'identification de version pour la classe de cet objet. Cet identifiant est appelé serialVersionUID et est calculé en fonction des informations relatives à la structure de la classe. Supposons que vous ayez créé une classe Employee et que son identifiant de version soit # 333 (attribué par la JVM). Maintenant, lorsque vous allez sérialiser l'objet de cette classe (objet Suppose Employee), la JVM lui attribue l'UID sous la forme # 333.

Considérez une situation. Dans le futur, vous devrez éditer ou changer votre classe et dans ce cas, lorsque vous la modifierez, JVM lui attribuera un nouvel UID (Suppose # 444). Désormais, lorsque vous essayez de désérialiser l'objet employé, JVM comparera l'ID de version (n ° 333) de l'objet sérialisé (employé) avec celui de la classe, c'est-à-dire n ° 444 (car il a été modifié). En comparaison, JVM trouvera que les deux UID de version sont différents et que, par conséquent, la désérialisation échouera. Par conséquent, si serialVersionID pour chaque classe est défini par le programmeur lui-même. Il en ira de même même si la classe évolue dans le futur. Par conséquent, JVM trouvera toujours que cette classe est compatible avec les objets sérialisés même si la classe est modifiée. Pour plus d'informations, vous pouvez vous reporter au chapitre 14 de HEAD FIRST JAVA.


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

Selon Josh, l'UID généré automatiquement est généré en fonction d'un nom de classe, des interfaces implémentées et de tous les membres publics et protégés. Toute modification de l’une quelconque de ces manières changera le serialVersionUID . Par conséquent, vous n'avez pas besoin de les manipuler uniquement si vous êtes certain qu'aucune version de la classe ne sera jamais sérialisée (à travers des processus ou extraite du stockage ultérieurement).

Si vous les ignorez pour l'instant et trouvez plus tard que vous devez modifier la classe tout en maintenant la compatibilité avec l'ancienne version de la classe, vous pouvez utiliser l'outil JDK serialver pour générer le serialVersionUID sur l' ancienne classe et définir explicitement que sur la nouvelle classe. (En fonction de vos modifications, vous devrez peut-être également implémenter une sérialisation personnalisée en ajoutant des méthodes writeObject et readObject - voir Javadoc Serializable ou le chapitre 11 susmentionné.)


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

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

La spécification d'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, parfois, vous souhaitez simplement, pour une raison quelconque, interdire la désérialisation d'anciens objets sérialisés [ backward incompatibility ], et dans ce cas, vous devez simplement modifier le paramètre serialVersionUID.

Les javadocs pour Serializable disent :

Le calcul série serialVersionUID par défaut est très sensible aux détails de classe qui peuvent varier en fonction de l'implémentation du compilateur et peut donc entraîner des InvalidClassException inattendues lors de la 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.


Il serait bien que CheckStyle puisse vérifier que le serialVersionUID d’une classe qui implémente Serializable a une bonne valeur, c’est-à-dire qu’il correspond à ce que le générateur d’identificateurs de version série produirait. Par exemple, si vous avez un projet avec de nombreux DTO sérialisables, n'oubliez pas de supprimer le serialVersionUID existant et de le régénérer est une tâche ardue. À l'heure actuelle, le seul moyen (à ma connaissance) de le vérifier est de régénérer chaque classe et de le comparer. le vieux. C'est très très douloureux.


Si vous n'avez jamais besoin de sérialiser vos objets dans un tableau d'octets et de les envoyer / les stocker, vous n'avez pas à vous en préoccuper. Si vous le faites, vous devez alors considérer votre serialVersionUID car le désérialiseur de l'objet le fera correspondre à la version de l'objet de son chargeur de classes. En savoir plus à ce sujet dans la spécification du langage Java.


Si vous sérialisez simplement parce que vous devez sérialiser pour l'implémentation (qui se soucie de savoir si vous sérialisez une session HTTPS, par exemple ... si elle est stockée ou non, vous ne vous inquiétez probablement pas de la désérialisation d'un objet de formulaire) , alors vous pouvez l'ignorer.

Si vous utilisez réellement la sérialisation, le fait de stocker et de récupérer des objets directement à l'aide de la sérialisation n'a plus d'importance. SerialVersionUID représente la version de votre classe. 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 directement la sérialisation. Si tel est le cas, générez un ID utilisateur sérialisable par défaut en cliquant sur l'option de réparation rapide et ne vous inquiétez pas.


Vous pouvez dire à 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 en signaler quelques-uns comme des erreurs), beaucoup sont très utiles:

  • Problèmes de programmation potentiels: affectation booléenne accidentelle possible
  • Problèmes de programmation potentiels: Accès nul au pointeur
  • Code inutile: la variable locale n'est jamais lue
  • Code inutile: contrôle nul redondant
  • Code inutile: transtypage inutile ou 'instanceof'

et beaucoup plus.


Les données de champ représentent certaines informations stockées dans la classe. Class implémente l'interface Serializable , eclipse a donc automatiquement proposé de déclarer le champ serialVersionUID . Commençons par la valeur 1 définie ici.

Si vous ne voulez pas que cet avertissement vienne, utilisez ceci:

@SuppressWarnings("serial")

SerialVersionUID est utilisé pour le contrôle de version d'un objet. vous pouvez également spécifier serialVersionUID dans votre fichier de classe. La conséquence de ne pas spécifier serialVersionUID est que, lorsque vous ajoutez ou modifiez un champ de la classe, la classe déjà sérialisée ne pourra pas être récupérée car serialVersionUID généré pour la nouvelle classe et pour l'ancien objet sérialisé sera différent. Le processus de sérialisation Java repose sur le serialVersionUID correct pour la récupération de l'état de l'objet sérialisé et lève l'exception java.io.InvalidClassException en cas de non concordance serialVersionUID

En savoir plus: http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ


Pour comprendre la signification du champ serialVersionUID, il convient de comprendre le fonctionnement de la sérialisation / désérialisation.

Lorsqu'un objet de classe Serializable est sérialisé, Java Runtime associe un numéro de version série (appelé en tant que serialVersionUID) à cet objet sérialisé. Au moment où vous désérialisez cet objet sérialisé, Java Runtime fait correspondre le serialVersionUID de l'objet sérialisé avec le serialVersionUID de la classe. Si les deux sont égaux, alors seul le processus de désérialisation supplémentaire est lancé, sinon l'instruction InvalidClassException est levée.

Nous concluons donc que pour que le processus de sérialisation / désérialisation aboutisse, le serialVersionUID de l'objet sérialisé doit être équivalent au serialVersionUID de la classe. Si le programmeur spécifie explicitement la valeur serialVersionUID dans le programme, la même valeur sera associée à l’objet sérialisé et à la classe, quelle que soit la plate-forme de sérialisation et de désérialisation (par exemple, la sérialisation peut être effectuée sur une plate-forme telle que windows en utilisant sun ou MS JVM et Deserialization peuvent se trouver sur différentes plateformes Linux utilisant Zing JVM).

Mais si le programme ne spécifie pas serialVersionUID, alors que Serialization \ DeSerialization n’importe quel objet, le moteur d’exécution Java utilise son propre algorithme pour le calculer. Cet algorithme de calcul serialVersionUID varie d'un JRE à un autre. Il est également possible que l'environnement dans lequel l'objet est sérialisé utilise un seul JRE (ex: SUN JVM) et que l'environnement dans lequel la désérialisation se produit utilise Linux Jvm (zing). Dans de tels cas, serialVersionUID associé à un objet sérialisé sera différent de serialVersionUID de la classe calculée dans l'environnement de désérialisation. À son tour, la désérialisation ne sera pas réussie. Donc, pour éviter de telles situations / problèmes, le programmeur doit toujours spécifier serialVersionUID de la classe Serializable.


Cette question est très bien documentée dans Effective Java de Joshua Bloch. Un très bon livre et à lire absolument. Je vais exposer certaines des raisons ci-dessous:

Le runtime de sérialisation fournit un numéro appelé Version série pour chaque classe sérialisable. Ce numéro s'appelle serialVersionUID. Il y a maintenant un peu de math derrière ce nombre et il est basé sur les champs / méthodes définis dans la classe. Pour la même classe, la même version est générée à chaque fois. Ce numéro est utilisé lors de la désérialisation pour vérifier que l'expéditeur et le destinataire d'un objet sérialisé ont des classes chargées pour cet objet compatibles avec la sérialisation. Si le destinataire a chargé une classe pour l'objet dont l'ID de série est différent de celui de la classe de l'expéditeur correspondante, la désérialisation entraîne une exception InvalidClassException.

Si la classe est sérialisable, vous pouvez également déclarer explicitement votre propre serialVersionUID en déclarant un champ nommé "serialVersionUID" qui doit être statique, final et de type long. La plupart des IDE comme Eclipse vous aident à générer cette longue chaîne.


Pourquoi utiliser SerialVersionUIDinside Serializableclass en Java?

Au cours de serializationl'exécution, Java crée un numéro de version pour une classe, afin de pouvoir la désérialiser ultérieurement. Ce numéro de version est connu sous le nom SerialVersionUIDde Java.

SerialVersionUIDest utilisé pour la version des données sérialisées. Vous ne pouvez désérialiser une classe que si elle SerialVersionUIDcorrespond à l'instance sérialisée. Lorsque nous ne déclarons pas SerialVersionUIDdans notre classe, le runtime Java le génère pour nous mais ce n'est pas recommandé. Il est recommandé de déclarer SerialVersionUIDcomme private static final longvariable pour éviter le mécanisme par défaut.

Lorsque vous déclarez une classe Serializableen implémentant une interface de marqueur java.io.Serializable, l'exécution Java de l'instance de cette classe est conservée sur le disque à l'aide du mécanisme de sérialisation par défaut, à condition que vous n'ayez pas personnalisé le processus à l'aide de l' Externalizableinterface.

voir aussi Pourquoi utiliser SerialVersionUID dans la classe Serializable en Java

Code: javassist.SerialVersionUID


J'utilise généralement serialVersionUID dans un seul contexte: lorsque je sais qu'il quittera le contexte de la machine virtuelle Java.

Je le saurais si j'utilisais ObjectInputStream et ObjectOutputStream pour mon application ou si je connaissais une bibliothèque / un framework que j'utiliserais l'utiliserait. SerialVersionID garantit que différentes machines virtuelles Java de versions différentes ou que différents fournisseurs interagiront correctement ou que, si elle est stockée et extraite en dehors de la machine virtuelle, par exemple HttpSession les données de session peuvent rester lors du redémarrage et de la mise à niveau du serveur d'applications.

Pour tous les autres cas, j'utilise

@SuppressWarnings("serial")

Depuis la plupart du temps, le serialVersionUID par défaut est suffisant. Cela inclut Exception , HttpServlet .


En ce qui concerne un exemple où le serialVersionUID manquant peut causer un problème:

Je travaille sur cette application Java EE composée d'un module Web utilisant un module EJB . Le module Web appelle le module EJB distance et transmet un POJO qui implémente Serializable en tant qu'argument.

POJO's classe de ce POJO's était empaquetée dans le fichier jar EJB et dans son propre fichier jar dans le module WEB-INF / lib du module Web. Ils appartiennent en fait à la même classe, mais lorsque je compresse le module EJB, je décompresse le fichier jar de ce POJO pour l’emballer avec le module EJB.

L'appel à l' EJB échouait avec l'exception ci-dessous car je n'avais pas déclaré son 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

Je dois d'abord expliquer la sérialisation.
La sérialisation permet de convertir l'objet en flux afin de l'envoyer sur le réseau OU de l'enregistrer dans un fichier OU de l'enregistrer dans une base de données pour une utilisation au format lettre.

Il existe des règles pour la sérialisation .

  • Un objet n'est sérialisable que si sa classe ou sa super-classe implémente l'interface Serializable

  • Un objet est sérialisable (implémente lui-même l'interface Serializable) même si sa super-classe ne l'est pas.Cependant, la première super-classe de la hiérarchie de la classe sérialisable, qui n'implémente pas l'interface Serializable, DOIT avoir un constructeur no-arg. Si cela est violé, readObject () produira une exception java.io.InvalidClassException lors de l'exécution

  • Tous les types primitifs sont sérialisables.

  • Les champs transitoires (avec modificateur transitoire) ne sont PAS sérialisés (c.-à-d. Qu'ils ne sont ni sauvegardés ni restaurés). Une classe qui implémente Serializable doit marquer les champs transitoires des classes qui ne prennent pas en charge la sérialisation (par exemple, un flux de fichiers).

  • Les champs statiques (avec modificateur statique) ne sont pas sérialisés.

Quand l'objet est sérialisé JAVA Runtime Associe le numéro de version de série appelé serialVersionID.

Où nous avons besoin serialVersionID: lors de la désérialisation pour vérifier que l'expéditeur et le destinataire sont compatibles en ce qui concerne la sérialisation.Si le destinataire a chargé la classe avec un autre ID sérieVersionID, la désérialisation se terminera par InvalidClassCastException .
Une classe sérialisable peut déclarer explicitement son propre serialVersionUID en déclarant un champ nommé «serialVersionUID» qui doit être statique, final et de type long :.

Essayons cela avec un exemple.

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();
}
}

Créer un objet sérialiser

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");
}
}

Deserializ l'objet

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());
 }
}    

REMARQUE: modifiez maintenant le serialVersionUID de la classe Employee et enregistrez:

private static final long serialVersionUID = **4L**;

Et exécutez la classe Reader. Ne pas exécuter la classe Writer et vous obtiendrez une exception.

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)

Ne vous embêtez pas, le calcul par défaut est très bon et suffit dans 99,9999% des cas. Et si vous rencontrez des problèmes, vous pouvez - comme déjà indiqué - introduire les UID comme une nécessité (ce qui est hautement improbable)







serialversionuid