Java 如何使用超类构造函数创建子类的实例?
显然,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
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,为我指出了这个有趣的细节。