众所周知,使用sun.misc.Unsafe#allocateInstance
可以创建一个对象,而无需调用任何类构造函数。
是否可以做相反的事情:给定一个现有实例,调用它的构造函数?
<小时/>澄清:这不是关于我在生产代码中执行的操作的问题。我对 JVM 内部结构和仍然可以完成的疯狂事情感到好奇。欢迎特定于某些 JVM 版本的答案。
最佳答案
JVMS §2.9禁止在已初始化的对象上调用构造函数:
Instance initialization methods may be invoked only within the Java Virtual Machine by the invokespecial instruction, and they may be invoked only on uninitialized class instances.
但是,在使用 JNI 初始化对象上调用构造函数仍然是技术上可行的。 CallVoidMethod函数 <init>
之间没有区别和普通的 Java 方法。此外,JNI 规范暗示 CallVoidMethod
可以用于调用构造函数,尽管它没有说明是否必须初始化实例:
When these functions are used to call private methods and constructors, the method ID must be derived from the real class of obj, not from one of its superclasses.
我已经验证以下代码可以在 JDK 8 和 JDK 9 中运行。JNI 允许您执行不安全的操作,但您不应在生产应用程序中依赖这一点。
ConstructorInvoker.java
public class ConstructorInvoker {
static {
System.loadLibrary("constructorInvoker");
}
public static native void invoke(Object instance);
}
constructorInvoker.c
#include <jni.h>
JNIEXPORT void JNICALL
Java_ConstructorInvoker_invoke(JNIEnv* env, jclass self, jobject instance) {
jclass cls = (*env)->GetObjectClass(env, instance);
jmethodID constructor = (*env)->GetMethodID(env, cls, "<init>", "()V");
(*env)->CallVoidMethod(env, instance, constructor);
}
TestObject.java
public class TestObject {
int x;
public TestObject() {
System.out.println("Constructor called");
x++;
}
public static void main(String[] args) {
TestObject obj = new TestObject();
System.out.println("x = " + obj.x); // x = 1
ConstructorInvoker.invoke(obj);
System.out.println("x = " + obj.x); // x = 2
}
}
关于java - 是否可以在现有实例上调用构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48616630/