JVMS §2.9 запрещает вызов конструктора на уже инициализированных объектах:
Методы инициализации экземпляра могут быть вызваны только внутри виртуальной машины Java с помощью инструкции invokespecial, и они могут быть вызваны только для неинициализированных экземпляров класса.
Однако по-прежнему технически возможно вызвать конструктор для инициализированного объекта с помощью JNI. CallVoidMethod не делает различий между <init>
и обычными методами Java. Более того, спецификация JNI намекает, что CallVoidMethod
может использоваться для вызова конструктора, хотя не говорит, должен ли экземпляр быть инициализирован или нет:
Когда эти функции используются для вызова закрытых методов и конструкторов, идентификатор метода должен быть получен из реального класса obj, а не из одного из его суперклассов.
Я убедился, что следующий код работает как в 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
}
}
person
apangin
schedule
05.02.2018