众所周知,使用sun.misc.Unsafe#allocateInstance可以创建一个对象而无需调用任何类构造函数。

是否有可能做相反的事情:给定现有实例,在其上调用构造函数?



澄清:这不是我在生产代码中要做的事情的问题。我对JVM内部和仍然可以完成的疯狂事情感到好奇。欢迎提供特定于某些JVM版本的答案。

最佳答案

JVMS §2.9禁止在已初始化的对象上调用构造函数:


  实例初始化方法只能在Java中调用
  通过invokespecial指令实现虚拟机,以及
  它们只能在未初始化的类实例上调用。


但是,从技术上讲,仍然可以使用JNI在初始化的对象上调用构造函数。 CallVoidMethod函数与<init>和普通Java方法没有区别。而且,JNI规范暗示了CallVoidMethod可以用于调用构造函数,尽管它没有说明是否必须初始化实例:


  当这些函数用于调用私有方法和构造函数时,方法ID必须从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
    }
}

关于java - 是否可以在现有实例上调用构造函数?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48616630/

10-17 02:42