Java serialization-java.io.InvalidClassException classe locale incompatible

J'ai une classe publique, qui implémente Serializable, qui est étendue par plusieurs autres classes. Seules ces sous-classes ont déjà été sérialisées auparavant-jamais la super classe.

La super classe avait défini un serialVersionUID.

Je ne suis pas sûr si cela compte, mais il n'était pas marqué privé, mais plutôt il avait juste la protection par défaut - vous pourriez dire qu'il était protégé par paquet

static final long serialVersionUID = -7588980448693010399L;

La super classe, ni aucune des sous-classes, cependant implémentée readObject ou writeObject, et aucune des sous-classes n'avait un serialVersionUID explicitement défini. J'ai pensé qu'un défini dans la superclasse serait suffisant.

Malgré tout cela, les choses allaient bien en ce qui concerne la lecture des objets sérialisés précédemment jusqu'à ce qu'une nouvelle variable d'instance, une List/ArrayList, ainsi qu'une nouvelle méthode soient ajoutées à la super classe et que certaines variables d'instance privées soient ajoutées à l'une de ses sous-classes.

Maintenant, lorsque vous essayez de lire des objets sérialisés précédemment, un exception est levée. Un semblable à ceci:

com.SomeCompany.SomeSubClass; local class incompatible: stream classdesc serialVersionUID = 1597316331807173261, local class serialVersionUID = -3344057582987646196

Je suppose que cela est dû au fait que le serialVersionUID par défaut, qui a été utilisé parce que je n'en ai pas déclaré un dans l'une des sous-classes, a maintenant changé en raison des changements dans la superclasse et une sous-classe.

Des Suggestions sur la façon de sortir de ce dilemme seraient appréciées. Je suppose que je dois implémenter readObject et writeObject, mais à part invoquer defaultReadObject() et defaultWriteObject(), Je ne le suis pas exactement ce que je dois faire. Je ne sais pas non plus si j'ai besoin d'ajouter serialVerisonUIDs à toutes les sous-classes ou si readObject et writeObject doivent être implémentés par chaque sous-classe, ou si je peux simplement les implémenter une fois, en supposant que j'en ai besoin, dans la superclasse.

47
demandé sur Dale 2011-12-01 06:21:21

4 réponses

@DanielChapman donne une bonne explication de serialVersionUID, mais pas de solution. la solution est: d'exécuter le serialver programme sur tous vos vieille classes. mettez ces valeurs serialVersionUID dans vos versions actuelles des classes. tant que les classes actuelles sont compatibles série avec les anciennes versions, vous devriez aller bien. (note pour l'avenir de code: vous devriez toujours ont serialVersionUID sur tous Serializable les classes)

Si les nouvelles versions n'est pas compatible série, alors vous devez faire de la magie avec une implémentation readObject personnalisée (vous n'auriez besoin que d'une writeObject personnalisée si vous essayiez d'écrire new class data qui serait compatible avec l'ancien code). d'une manière générale, l'ajout ou la suppression de champs de classe ne rend pas une classe série incompatible. changer le type de champs existants sera généralement.

Bien sûr, même si la nouvelle classe est série compatible, vous pouvez toujours personnalisé readObject application. vous pouvez le vouloir Si vous voulez remplir de nouveaux champs manquants dans les données enregistrées à partir d'anciennes versions de la classe (par exemple, vous avez un nouveau champ de liste que vous souhaitez initialiser dans une liste vide lors du chargement d'anciennes données de classe).

42
répondu jtahlborn 2017-05-23 11:55:07

La réponse courte ici est que l'ID série est calculé via un hachage si vous ne le spécifiez pas. (Les membres statiques ne sont pas hérités-ils sont statiques, il n'y a que (1) et il appartient à la classe).

Http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html

La méthode getSerialVersionUID renvoie le serialVersionUID de cette classe. Reportez-vous à la Section 4.6, " identificateurs uniques de flux."Si ce n' spécifiée par la classe, la valeur renvoyée est un hachage calculé à partir de la nom, interfaces, méthodes et champs de la classe utilisant le hachage sécurisé Algorithme (SHA) tel que défini par L'Institut National des normes.

Si vous modifiez une classe ou sa hiérarchie, votre hachage sera différent. C'est une bonne chose. Vos objets sont différents maintenant qu'ils ont des membres différents. En tant que tel, si vous le relisez à partir de sa forme sérialisée, il s'agit en fait d'un objet différent-donc de l'exception.

La réponse longue est la sérialisation est extrêmement utile, mais ne devrait probablement pas être utilisé pour la persistance à moins qu'il n'y ait pas d'autre moyen de le faire. C'est un chemin dangereux spécifiquement à cause de ce que vous vivez. Vous devriez envisager une base de données, XML, un format de fichier et probablement une JPA ou une autre structure de persistance pour un projet Java pur.

26
répondu Daniel B. Chapman 2011-12-01 03:03:14

Pour moi, j'ai oublié d'ajouter l'id série par défaut.

private static final long serialVersionUID = 1L;
15
répondu taxeeta 2013-09-20 07:15:36

Cela a fonctionné pour moi:

Si vous avez écrit votre objet de classe sérialisé dans un fichier, puis apporté quelques modifications au fichier et l'avez compilé, puis que vous essayez de lire un objet, cela se produira.

Donc, écrivez à nouveau les objets nécessaires au fichier si une classe est modifiée et recompilée.

PS: ce n'est pas une solution; était censé être une solution de contournement.

6
répondu skrtbhtngr 2017-09-24 17:52:15