В Java дано
Class c = ...
Мы можем создать объект этого класса, предварительно получив конструктор. Например, если мы хотим использовать конструктор по умолчанию (без параметров),
c.getConstructor().newInstance()
Это кажется простым и похоже на то, как все делается в исходном коде Java.
Но, что любопытно, в байт-коде JVM дела обстоят иначе. Там создание объекта выполняется в два этапа: new
для фактического создания объекта, затем invokespecial
для вызова соответствующего конструктора.
Есть ли способ обойти конструктор, когда у вас есть Class
(с фактическим классом, который должен быть определен во время выполнения)? Если нет, то было ли когда-либо документировано обоснование разницы между тем, как это работает, и тем, как работает байт-код?
Вы хотите выделить неинициализированный объект.
Вы можете попробовать библиотеку под названием Objenesis.
В противном случае вы можете создать объект путем сериализации. Это широко используемый метод для создания неинициализированного объекта.
public class Serialization {
static class TestSerialization implements Serializable {
int val = 0;
public TestSerialization() {
System.out.println("constructor");
val = 1;
}
@Override
public String toString() {
return "val is " + val;
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
TestSerialization testSerialization = new TestSerialization();
// constructor
// val is 1
System.out.println(testSerialization);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(testSerialization);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
Object obj = ois.readObject();
// val is 1
System.out.println(obj);
}
}
На шаг ближе вы можете использовать ReflectionFactory для создания пустого неинициализированного объекта.
public class Main {
static class TestClass {
public int val = 0;
public TestClass() {
val = 1;
}
@Override
public String toString() {
return "value is " + val;
}
}
public static void main(String[] args) throws Exception {
// by constructor
TestClass obj = new TestClass();
// value is 1
System.out.println(obj);
// by reflect
Constructor<TestClass> constructor = TestClass.class.getConstructor();
obj = constructor.newInstance();
// value is 1
System.out.println(obj);
// by ReflectionFactory
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
Constructor<Object> objectConstructor = Object.class.getDeclaredConstructor();
Constructor<?> targetConstructor = reflectionFactory.newConstructorForSerialization(TestClass.class, objectConstructor);
obj = (TestClass) targetConstructor.newInstance();
// value is 0
System.out.println(obj);
}
}