Sérialisation Java avec initialisation statique
-
09-06-2019 - |
Question
En Java, les champs statiques et transitoires ne sont pas sérialisés.Cependant, j'ai découvert que l'initialisation des champs statiques entraînait la modification du SerialVersionUID généré.Par exemple, static int MYINT = 3;
entraîne la modification du SerialVersionUID.Dans cet exemple, cela est logique car différentes versions de la classe obtiendraient des valeurs initiales différentes.Pourquoi toute initialisation modifie-t-elle le SerialVersionUID ?Par exemple, static String MYSTRING = System.getProperty("foo");
entraîne également la modification du SerialVersionUID.
Pour être précis, ma question est de savoir pourquoi l'initialisation avec une méthode entraîne la modification du SerialVersionUID.Le problème que j'ai rencontré est que j'ai ajouté un nouveau champ statique initialisé avec une valeur de propriété système (getProperty).Ce changement a provoqué une exception de sérialisation lors d'un appel distant.
La solution
Vous pouvez trouver des informations à ce sujet dans le bogue 4365406 et dans le algorithme pour l'informatique SerialVersionUID.Fondamentalement, lorsque vous modifiez l'initialisation de votre static
membre avec System.getProperty()
, le compilateur introduit un nouveau static
propriété de votre classe faisant référence au System
classe (je suppose que le System
n'était pas référencée auparavant dans votre classe), et comme cette propriété introduite par le compilateur n'est pas privée, elle participe à la serialVersionUID
calcul.
Moralité:utilisez toujours explicite serialVersionUID
, vous économiserez quelques cycles CPU et quelques maux de tête :)
Autres conseils
Le SerialVersionUID automatique est calculé en fonction des membres d’une classe.Ceux-ci peuvent être affichés pour un fichier de classe à l'aide de l'outil javap du Sun JDK.
Dans le cas mentionné dans la question, le membre ajouté/supprimé est l'initialiseur statique.Cela apparaît comme ()V dans les fichiers de classe.Le contenu de la méthode peut être désassemblé en utilisant javap -c.Vous devriez être en mesure de distinguer l'appel System.getProperty("foo") et l'affectation à MYSTRING.Cependant, une affectation avec une chaîne littérale (ou toute constante de compilation telle que définie par la spécification du langage Java) est prise en charge directement par le fichier de classe, éliminant ainsi le besoin d'un initialiseur statique.
Un cas courant de code ciblant J2SE 1.4 (utilisez -source 1.4 -target 1.4) ou une version antérieure est celui des champs statiques des anciennes instances de classe qui apparaissent comme des littéraux de classe dans le code source (MyClass.class).L'instance de classe est recherchée à la demande avec Class.forName et stockée dans un champ statique.C'est ce champ statique qui perturbe le serialVersionUID.Depuis J2SE 5.0, une variante de l'opcode ldc prend directement en charge les littéraux de classe, supprimant ainsi le besoin du champ synthétique.Encore une fois, tout cela peut être affiché avec javap -c.
Si j'ai lu correctement les spécifications, l'automatique serialVersionUID
ne devrait pas changer si vous modifiez la valeur d'un champ statique ou transitoire.Jeter un coup d'œil à Chapitre 5.6 de la Spéc.
Cependant, si vous y réfléchissez un peu, vous commencez par sérialiser un objet qui a static int MYINT = 3
, lorsque vous désérialisez ensuite la classe, vous espérez récupérer le même objet, c'est-à-dire avec MYINT = 3
.Ainsi, si vous modifiez l'initialisation statique, vous vous attendez à ce que le serialVersionUID
changer car vous ne pouvez pas récupérer le même objet.
De toute façon, conservez-le dans toutes vos classes sérialisables et vous pourrez contrôler le serialVersionUID
:
private static final long serialVersionUID = 7526472295622776147L;
J'ai mis à jour la question pour être plus claire.Je comprends pourquoi l'initialisation avec un littéral change le serialVersionUID
mais pas pourquoi l'initialisation dynamique le change.Si vous initialisez avec une méthode, la valeur, bien entendu, peut toujours être différente.
Réglage du serialVersionUID
explicitement, cela convient dans une version ultérieure de la classe uniquement si vous êtes sûr qu'il s'agit d'un changement sûr.