web-dev-qa-db-fra.com

Java sérialisation - classe locale Java.io.InvalidClassException 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 - jamais la super classe.

La super classe avait défini un serialVersionUID.

Je ne sais pas si cela compte, mais cela n'a pas été marqué comme privé, mais plutôt comme protection par défaut - vous pourriez dire que le paquet était protégé

static final long serialVersionUID = -7588980448693010399L;

La super-classe, ni aucune des sous-classes, ont cependant implémenté readObject ou writeObject, et aucune des sous-classes n’avait un serialVersionUID défini explicitement. Je pensais qu'une définition dans la super-classe serait suffisante.

Malgré tout, les choses allaient bien jusqu'à relire des objets précédemment sérialisés jusqu'à ce qu'une nouvelle variable d'instance, une liste/un tableau, 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. .

Désormais, lors d'une tentative de lecture d'objets précédemment sérialisés, une exception est généré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 nom de série serialVersionUID par défaut, qui a été utilisé parce que je n'en ai déclaré aucun dans l'une des sous-classes, a maintenant été modifié en raison des modifications apportées à 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 mis à part l'appel de defaultReadObject () et defaultWriteObject (), je ne sais pas exactement ce que je dois faire. Je ne sais pas non plus si j'ai besoin d'ajouter des serialVerisonUID à toutes les sous-classes, ni 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.

54
Dale

@ DanielChapman donne une bonne explication de serialVersionUID, mais pas de solution. la solution est la suivante: exécutez le programme serialver sur toutes vos anciennes classes . mettez ces serialVersionUID valeurs dans vos versions actuelles des classes. tant que les classes actuelles sont compatibles série avec les anciennes versions, tout devrait bien se passer. (note pour le code futur: vous devriez toujours avoir un serialVersionUID sur tout Serializable classes)

si les nouvelles versions ne sont pas compatibles en série, vous devez alors faire un peu de magie avec une implémentation personnalisée readObject (vous n'auriez besoin que d'une custom writeObject si vous tentiez d'écrire de nouvelles données de classe compatibles avec l'ancien code). en règle générale, l'ajout ou la suppression de champs de classe ne rend pas une série sérielle incompatible. changer le type de champs existants le fera habituellement.

Bien sûr, même si la nouvelle classe est compatible série, vous souhaiterez peut-être une implémentation personnalisée de readObject. vous souhaiterez peut-être cela si vous souhaitez renseigner les nouveaux champs manquants dans les données sauvegardées 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 des anciennes données de la classe).

45
jtahlborn

La réponse courte ici est que l'ID de 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 elle n'est pas spécifiée par la classe, la valeur renvoyée est un hachage calculé à partir du nom, des interfaces, des méthodes et des champs de la classe, à l'aide de l'algorithme SHA (Secure Hash Algorithm) défini par le National Institute of Standards.

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 - c'est donc l'exception.

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

29
Daniel B. Chapman

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

private static final long serialVersionUID = 1L;
18
taxeeta

Cela a fonctionné pour moi:

Si vous avez écrit votre objet de classe Serialized dans un fichier, puis l'avez modifié et compilé, puis essayez de lire un objet, cela se produira.

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

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

6
skrtbhtngr