How does java serialization deserialize final fields when no default constructor specified?

26,922

Solution 1

Deserialization is implemented by the JVM on a level below the basic language constructs. Specifically, it does not call any constructor.

Solution 2

Given that the class doesn't have a no arg constructor, how can it be instantiated and the final field set?

Some nasty black magic happens. There is a backdoor in the JVM that allows an object to be created without invoking any constructor. The fields of the new object are first initialized to their default values (false, 0, null, etc), and then the object deserialization code populates the fields with values from the object stream.

(Now that Java is open sourced, you can read the code that does this ... and weep!)

Solution 3

Both Michael and Stephen gave you an excellent answer, I just want to caution you about transient fields.

If default value (null for references, 0 for primitives ) is not acceptable for them after deserialization then you have to provide your version of readObject and initialize it there.

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

        // derivedValue is still 0
        this.derivedValue = derivedValue( value );
    }
Share:
26,922
mdma
Author by

mdma

Updated on July 09, 2022

Comments

  • mdma
    mdma almost 2 years

    I have an class defining an immutable value type that I now need to serialize. The immutability comes from the final fields which are set in the constructor. I've tried serializing, and it works (surprisingly?) - but I've no idea how.

    Here's an example of the class

    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...
    }
    

    Given that the class doesn't have a no arg constructor, how can it be instantiated and the final field set?

    (An aside - I noticed this class particularly because IDEA wasn't generating a "no serialVersionUID" inspection warning for this class, yet successfully generated warnings for other classes that I've just made serializable.)

  • Oak
    Oak about 14 years
    Actually, it does call the parameterless constructor. Only classes with such a constructor are serializable.
  • Alexander Pogrebnyak
    Alexander Pogrebnyak about 14 years
    @Oak. Not true. You can serialize a class without a default constructor.
  • gustafc
    gustafc about 14 years
    In fact, you don't even need nasty black magic, reflection (a.k.a. nasty gray magic ;) can be used to set final fields. It works as long as the values are set before the fields are read the first time.
  • Oak
    Oak about 14 years
    @Alexander: my bad. I've confused it with the fact that non-serializable superclasses to serializable types must have such a constructor.
  • Stephen C
    Stephen C about 14 years
    @gustafc - but black magic is needed to create the object without executing any constructors.
  • Brett Kail
    Brett Kail about 14 years
    @Oak: right, so Michael's answer is actually slightly inaccurate (not that it matters for the purpose of this question). Deserialization does call the constructor of the last non-serializable class in the hierarchy.
  • Oliv
    Oliv over 5 years
    You can also assign a value at field declaration, if it's sufficient.
  • Stephen C
    Stephen C almost 4 years
    ... which is important because execution of any of the constructors may have unwanted side-effects. And anyhow, the >>spec<< says that no constructors are invoked in the simple case where the superclass chain is serializable.
  • Daniel Alder
    Daniel Alder almost 4 years
    too bad if this transient field is final. for example a randomly generated uuid