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.
