1

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.

Oleg Pyzhcov
  • 7,133
  • 1
  • 16
  • 30

4 Answers4

4

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
    }
}
apangin
  • 86,266
  • 9
  • 178
  • 218
  • 2
    Now the really interesting question… what will happen if `TestObject` has a nontrivial `finalize()` method? – Holger Feb 05 '18 at 16:47
  • 1
    @Holger Excellent question! By default, HotSpot JVM registers finalizers at the end of `Object.`. That is, `finalize()` can be called twice for the same object. With `-XX:-RegisterFinalizersAtInit` flag finalizers are registered right after the object allocations, so `finalize()` will be called just once. – apangin Feb 05 '18 at 17:59
  • Oh. It’s interesting that there is an option to alter that behavior, as that seems to contradict the specification, [JLS §12.6.1](https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.6.1-330): “*An object o is not finalizable until its constructor has invoked the constructor for Object on o and that invocation has completed successfully (that is, without throwing an exception).*” It doesn’t look like this can still be guaranteed if the object is already registered at allocation. – Holger Feb 05 '18 at 18:06
2

A constructor is not an instance method, so no you can't invoke a constructor on an instance.

If you look at the reflection library, you'll see that the return type of Class.getConstructor() is Constructor, which doesn't have any methods that can accept a instance - its only relevant method is newInstance(), which doesn't accept a target instance; it creates one.

On the other hand, the return type of Class.getMethod() is Method, whose first parameter is the instance.

A Constructor is not a Method.

Bohemian
  • 389,931
  • 88
  • 552
  • 692
  • Thank you for your answer. I know that part about regular reflection API. However, in JVM bytecode instance creation is two-fold: `new`, to allocate memory, and `invokespecial`, to call a constructor. So it might be possible in theory to achieve what I want – Oleg Pyzhcov Feb 05 '18 at 06:21
1

In the JVM spec for invokespecial:

An invokespecial instruction is type safe iff all of the following are true:

... (Stuff about non-init methods)

  • MethodName is <init>.
  • Descriptor specifies a void return type.
  • One can validly pop types matching the argument types given in Descriptor and an uninitialized type, UninitializedArg, off the incoming operand stack, yielding OperandStack.
  • ...

If you've already initialized the instance, it's not an uninitialized type, so this will fail.

Note that other invoke* instructions (invokevirtual, invokeinterface, invokestatic, invokedynamic) explicitly preclude invocation of <init> methods, so invokespecial is the only way to invoke them.

Andy Turner
  • 131,952
  • 11
  • 151
  • 228
0

From JLS Sec 8.8

Constructors are invoked by class instance creation expressions (§15.9), by the conversions and concatenations caused by the string concatenation operator +(§15.18.1), and by explicit constructor invocations from other constructors (§8.8.7). 

...

Constructors are never invoked by method invocation expressions (§15.12).

So no, it's not possible.

If there is some common action you want to take in the constructor and elsewhere, put it into a method, and invoke that from the constructor.

Andy Turner
  • 131,952
  • 11
  • 151
  • 228
  • Thanks for your answer. The JLS, however, only covers "normal" Java. E.g. it says `... it is possible to prevent class instantiation by declaring an inaccessible constructor`, without considering reflection's `setAccessible(true)` or `Unsafe`. With these things in mind, it's not at all possible to prevent class instantiation. My question is about the same level of unsafe manipulations. – Oleg Pyzhcov Feb 05 '18 at 06:40
  • Having made the constructor visible, you can still only invoke it in one of the quoted ways. – Andy Turner Feb 05 '18 at 06:46
  • @Oleg it is possible to prevent class instantiation, even if employing reflection by declaring a (typically `private`) constructor that throws an exception. See [this answer](https://stackoverflow.com/a/14077876/256196) – Bohemian Feb 05 '18 at 06:49
  • @Bohemian you can still create an instance using `Unsafe`, since, as I've already mentioned, that would not invoke your constructor. – Oleg Pyzhcov Feb 05 '18 at 06:51