Apparently Java serialization mechanism somehow manages to create an instance of subclass using superclass constructor. I wonder, how is it possible?
Here's a test which demonstrates this:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.MessageFormat;
public class Test {
public static class A {
public final int a;
public A() {
this.a = 0;
System.out.println(
MessageFormat.format(
"new A() constructor is called to create an instance of {0}.",
getClass().getName()));
}
public A(int a) {
this.a = a;
System.out.println(
MessageFormat.format(
"new A(int) constructor is called to create an instance of {0}.",
getClass().getName()));
}
}
public static class B extends A implements Serializable {
public final int b;
public B(int a, int b) {
super(a);
this.b = b;
System.out.println(
MessageFormat.format(
"new B(int, int) constructor is called to create an instance of {0}.",
getClass().getName()));
}
@Override
public String toString() {
return "B [a=" + a + ", b=" + b + "]";
}
}
public static void main(String[] args) throws Exception {
B b1 = new B(10,20);
System.out.println(b1);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try(ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(b1);
}
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
try (ObjectInputStream ois = new ObjectInputStream(bis)) {
B b2 = (B)ois.readObject();
System.out.println(b2);
}
}
}
Output:
new A(int) constructor is called to create an instance of Test$B.
new B(int, int) constructor is called to create an instance of Test$B.
B [a=10, b=20]
new A() constructor is called to create an instance of Test$B.
B [a=0, b=20]
(You can try it out live on Ideone).
As you see, the A() constructor is called during deserialization to produce an instance of B. Under the hood this is invoked in ObjectStreamClass.newInstance() and the instance is created by the Constructor.newInstance() call. In the debugger, the constructor cons is Test$A():
Stepping out in the debugger, the created object is finally returned from ObjectInputStream.readObject(...) and it is casted without problems to B.
So if I am not mistaken, it seems that the A() constructor was used (via reflection) to create an instance of B.
I wonder how is this possible.

new Type()first creates an instructionNEW Typeand then calls the constructor withINVOKESPECIAL ...on this instance. So a constructor always expects the object of the final type on the stack. Calling super does not create an object of B, it receives it.cons.newInstance();would know what the "final type" is.consisTest$A(), I don't see whereBis involved here at all.java.reflect.Constructor:private volatile ConstructorAccessor constructorAccessor. It is filled with an object of typeGeneratedSerializationConstructorAccesor1@.... Its use can be found inConstructor.newInstance