Search code examples
javareflectionjvmunsafe

Is it possible to call constructor on existing instance?


It is known that using sun.misc.Unsafe#allocateInstance one can create an object without calling any class constructors.

Is it possible to do the opposite: given an existing instance, invoke a constructor on it?


Clarification: this is not the question about something I'd do in production code. I'm curious about JVM internals and crazy things that can still be done. Answers specific to some JVM version are welcome.


Solution

  • JVMS §2.9 forbids invocation of constructor on already initialized objects:

    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.

    However, it is still technically possible to invoke constructor on initialized object with JNI. CallVoidMethod function does not make difference between <init> and ordinary Java methods. Moreover, JNI specification hints that CallVoidMethod may be used to call a constructor, though it does not say whether an instance has to be initialized or not:

    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.

    I've verified that the following code works both in JDK 8 and JDK 9. JNI allows you to do unsafe things, but you should not rely on this in production applications.

    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
        }
    }