33 votes

Comment java serialization désérialise-t-il les champs finaux lorsqu'aucun constructeur par défaut n'est spécifié?

J'ai une classe définissant un type de valeur immuable que je dois maintenant sérialiser. L'immutabilité vient des champs finaux qui sont définis dans le constructeur. J'ai essayé de sérialiser, et ça fonctionne (étonnamment?) - mais je n'ai aucune idée 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 sans argument, comment peut-elle être instanciée et le champ final être défini?

(Au fait - j'ai remarqué cette classe en particulier parce qu'IDEA ne générait pas d'avertissement "pas de serialVersionUID" pour cette classe, alors qu'il a généré avec succès des avertissements pour d'autres classes que je viens de rendre sérialisables.)

27voto

Michael Borgwardt Points 181658

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

12voto

Stephen C Points 255558

Étant donné que la classe n'a pas de constructeur sans arguments, comment peut-elle être instanciée et le champ final défini ?

De la magie noire déplaisante se produit. Il y a une porte dérobée dans la JVM qui permet de créer un objet sans invoquer de constructeur. Les champs du nouvel objet sont d'abord initialisés à leurs valeurs par défaut (false, 0, null, etc), puis le code de désérialisation de l'objet peuple les champs avec les valeurs du flux d'objets.

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

6voto

Alexander Pogrebnyak Points 24964

Les deux Michael et Stephen vous ont donné une excellente réponse, je tiens juste à vous mettre en garde concernant les champs transitoires.

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, alors vous devez fournir votre propre version de la méthode readObject et l'initialiser là-bas.

    private void readObject (
            final ObjectInputStream s
        ) throws
            ClassNotFoundException,
            IOException
    {
        s.defaultReadObject( );

        // derivedValue est toujours à 0
        this.derivedValue = derivedValue( value );
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X