如何使用超类构造函数创建子类的实例?

时间:2018-04-15 14:53:59

标签: java reflection constructor

显然,Java序列化机制以某种方式设法使用超类构造函数创建子类的实例。我想知道,怎么可能?

这里有一个test来证明这一点:

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);
        }
    }
}

输出:

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]

(你可以try it out live on Ideone)。

如您所见,在反序列化期间调用A()构造函数以生成B的实例。在引擎盖下,这在ObjectStreamClass.newInstance()中调用,实例由Constructor.newInstance()调用创建。在调试器中,构造函数consTest$A()

Screenshot from the the debugger showing that <code>cons</code> is <code>Test$A()</code>

退出调试器后,最终会从ObjectInputStream.readObject(...)返回创建的对象,并且它会毫无问题地投放到B

因此,如果我没有弄错的话,似乎使用A()构造函数(通过反射)来创建B的实例。

我想知道这怎么可能。

2 个答案:

答案 0 :(得分:2)

我怀疑构造函数cons一定有问题。我找到了A的普通构造函数更改为serializable constructor B的位置。

首先,我查看了cons首次设置的位置。在序列化的情况下,这是ObjectStreamClass

的构造函数
if (externalizable) {
   cons = getExternalizableConstructor(cl);
} else {
   cons = getSerializableConstructor(cl); //here
   ...

所以我介入并找到ObjectStreamClass.getSerializableConstructor

Constructor<?> cons = initCl.getDeclaredConstructor((Class<?>[]) null);
...
cons = reflFactory.newConstructorForSerialization(cl, cons); //this does change
cons.setAccessible(true);
return cons;

将调试监视放在cons.newInstance()

  • 在标记线=&gt;之前类型为A
  • 标记后的行=&gt;类型为B

这意味着用于序列化的构造函数不是A的普通构造函数,而是用于序列化的修改构造函数,它适用于最终类。

答案 1 :(得分:0)

反序列化期间的JVM不会调用被反序列化的类的构造函数。但是为了创建反序列化类的实例,首先需要创建它的超类。因此,JVM调用没有实现Serializable的第一个父项的no args构造函数。虽然它不会在此构造函数中创建类的实例。如果您的父类是Serializable,则根本没有构造函数调用。