【发布时间】:2018-09-25 08:46:32
【问题描述】:
显然,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() 调用创建的。在调试器中,构造函数cons为Test$A():
在调试器中跳出,创建的对象最终从ObjectInputStream.readObject(...)返回,并毫无问题地转换为B。
所以如果我没记错的话,似乎是使用A() 构造函数(通过反射)来创建B 的实例。
我想知道这怎么可能。
【问题讨论】:
-
使用字节码查看器:
new Type()首先创建指令NEW Type,然后在此实例上使用INVOKESPECIAL ...调用构造函数。所以构造函数总是期望堆栈上的最终类型的对象。调用 super 不会创建 B 的对象,而是接收它。 -
@CoronA 这听起来合乎逻辑,但我不明白
cons.newInstance();怎么知道“最终类型”是什么。cons是Test$A(),我根本看不出这里涉及到B。 -
我会指向
java.reflect.Constructor中的一个字段:private volatile ConstructorAccessor constructorAccessor。它填充了GeneratedSerializationConstructorAccesor1@...类型的对象。它的使用可以在Constructor.newInstance中找到 -
@CoronA 越来越近了,但我还没有完整的图片。
-
@CoronA 我认为这个构造函数访问器只是“按字节码生成反射优化”。还不清楚。
标签: java reflection constructor