Comment la sérialisation Java désérialise-t-elle les champs finaux quand aucun constructeur par défaut n’a été spécifié?

J’ai une classe définissant un type de valeur immuable que je dois maintenant sérialiser. L’immutabilité provient des derniers champs définis dans le constructeur. J’ai essayé la sérialisation, et ça marche (étonnamment?) – mais je ne sais pas comment.

Voici un exemple de la classe

public class MyValueType implements Serializable { private final int value; private transient int derivedValue; public MyValueType(int value) { this.value = value; this.derivedValue = derivedValue(value); } // getters etc... } 

Étant donné que la classe n’a pas de constructeur no arg, comment peut-il être instancié et le champ final défini?

(Un côté – j’ai remarqué cette classe en particulier parce que IDEA ne générait pas un avertissement d’inspection “no serialVersionUID” pour cette classe, mais avait généré avec succès des avertissements pour d’autres classes que je viens de rendre sérialisables.)

La désérialisation est implémentée par la JVM à un niveau inférieur aux constructions de langage de base. Plus précisément, il n’appelle aucun constructeur.

Étant donné que la classe n’a pas de constructeur no arg, comment peut-il être instancié et le champ final défini?

Une méchante magie noire se produit. Il existe une porte dérobée dans la machine virtuelle Java qui permet de créer un object sans appeler de constructeur. Les champs du nouvel object sont d’abord initialisés à leurs valeurs par défaut (false, 0, null, etc.), puis le code de désérialisation de l’object remplit les champs avec des valeurs du stream d’objects.

(Maintenant que Java est open source, vous pouvez lire le code qui le fait … et pleurer!)

Michael et Stephen vous ont tous deux donné une excellente réponse, je veux juste vous mettre en garde contre transient champs transient .

Si la valeur par défaut ( null pour les références, 0 pour les primitives) n’est pas acceptable pour eux après la désérialisation, vous devez fournir votre version de readObject et l’initialiser ici.

  private void readObject ( final ObjectInputStream s ) throws ClassNotFoundException, IOException { s.defaultReadObject( ); // derivedValue is still 0 this.derivedValue = derivedValue( value ); }