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

Java 如何使用超类构造函数创建子类的实例?,java,reflection,constructor,Java,Reflection,Constructor,显然,Java序列化机制设法使用超类构造函数创建子类的实例。我想知道,这怎么可能 下面是一个例子,说明了这一点: import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.Mes

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

下面是一个例子,说明了这一点:

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]
(你可以)


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

跳出调试器,创建的对象最终从
ObjectInputStream.readObject(…)
返回,并将其毫无问题地强制转换到
B

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


我想知道这是怎么可能的。

反序列化期间的JVM不会调用被反序列化的类的构造函数。但是为了创建反序列化类的实例,它需要首先创建它的超类。因此JVM调用第一个父级的无参数构造函数,该构造函数不实现
Serializable
。尽管它不会在此构造函数中创建类的实例。如果父类是可序列化的,则根本没有构造函数调用。

我怀疑构造函数肯定出了问题。我找到了
A
的普通构造函数变为
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;
Constructor=initCl.getDeclaredConstructor((类[])null);
...
cons=reflFactory.newConstructorForSerialization(cl,cons)//这确实改变了
cons.setAccessible(true);
返回cons;
cons.newInstance()上放置调试监视

  • 在标记行之前=>类型为
    A
  • 在标记行=>之后,类型为
    B

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

使用字节码查看器:
新类型()
首先创建一条指令
新类型
,然后在此实例上使用
INVOKESPECIAL…
调用构造函数。因此,构造函数总是期望堆栈上的最终类型的对象。调用super不会创建B的对象,而是接收它。@CoronA这听起来合乎逻辑,但我不知道
cons.newInstance()如何将知道“最终类型”是什么
cons
Test$A()
,我根本看不出
B
在这里涉及到什么。我会指向
java.reflect.Constructor
中的一个字段:
私有易失性构造函数或Accessor构造函数或Accessor
。它由类型为
GeneratedSerializationConstructorAccesor1@...
。它的用法可以在
构造函数中找到。newInstance
@CoronA越来越近了,但我还没有完全了解。@CoronA我认为这个构造函数访问器只是“通过字节码生成优化反射”。仍然不清楚。
ObjectStreamClass.newInstance()
确实通过反射调用了
Test$A()
构造函数。我想知道结果怎么会是
B
。这不是最终结果。它是关于JVM如何实现继承和序列化的。要创建对象,JVM应该创建其所有父对象。可序列化的父级是在没有构造函数调用的情况下创建的。但是JVM不能在没有构造函数调用的情况下创建不可序列化的父级。所以JVM使用构造函数创建不可序列化的父级,然后在没有构造函数调用的情况下继续反序列化是JVM的一部分吗?我在
ObjectInputStream
和co中看不到任何JVM魔力。一些反射用法,但没有什么特别的。所以JVM使用构造函数创建不可序列化的父级,然后继续反序列化而不调用构造函数-好的,让我们把它留给JVM。但是,使用
Test$A()
构造函数创建的实例是
B
的实例,这是如何工作的呢?因为这就是这里发生的事情。我暂时不接受你的回答,我会给你一笔赏金,然后再次接受。非常感谢你的坚持。也许投票人可以暗示他/她的期望没有得到满足?谢谢@lexicore,为我指出了这个有趣的细节。