Создание парадокса
Любые мысли о создании класса Paradox без изменения самого класса?
public final class Paradox {
private final Paradox paradox;
public Paradox(Paradox paradox) {
this.paradox = paradox;
if (this.paradox == null) throw new InceptionException();
}
private static class InceptionException extends RuntimeException {
public InceptionException() {
super("Paradox requires an instance of paradox to be instantiated");
}
}
}
Ответы
Ответ 1
Как уже указывалось в комментариях: возможно с классом sun.misc.Unsafe
:
import java.lang.reflect.Constructor;
import sun.misc.Unsafe;
public class ParadoxTest
{
public static void main(String[] args) throws Exception
{
Constructor<Unsafe> unsafeConstructor =
Unsafe.class.getDeclaredConstructor();
unsafeConstructor.setAccessible(true);
Unsafe unsafe = unsafeConstructor.newInstance();
Paradox paradox = (Paradox) unsafe.allocateInstance(Paradox.class);
System.out.println("This is paradox: "+paradox);
}
}
В качестве альтернативы можно использовать функцию JNI AllocObject
, которая также выделяет новый объект без вызова любого из конструкторов.
EDIT: Кто-то нашел другое решение - без sun.misc.Unsafe
!
EDIT2: Обратите внимание, что это решение также использует собственные классы Sun, которые являются не частью публичного API, и поэтому это решение по-прежнему не переносимо и должно not будет использоваться в производственном коде!
import java.lang.reflect.Constructor;
import sun.reflect.ReflectionFactory;
public class AnotherParadoxTest
{
public static void main(String[] args) throws Exception
{
ReflectionFactory rf = ReflectionFactory.getReflectionFactory();
Constructor<?> declaredConstructor =
Object.class.getDeclaredConstructor();
Constructor<?> constructor = rf.newConstructorForSerialization(
Paradox.class, declaredConstructor);
Paradox paradox = (Paradox) constructor.newInstance();
System.out.println("This is paradox: "+paradox);
}
}
EDIT3: только для полноты и для каждого запроса в комментариях: решение на основе JNI
Как упоминалось выше, функция JNI AllocObject
не вызывает конструктор, поэтому это также можно использовать для создания экземпляра такого объекта. Это вызывающий класс, который содержит собственный метод и загружает собственную библиотеку:
public class ParadoxTestWithJni
{
static
{
System.loadLibrary("Paradox");
}
public static void main(String[] args)
{
Paradox paradox = createParadox();
System.out.println("This is paradox: "+paradox);
}
private native static Paradox createParadox();
}
Заголовок ParadoxTestWithJni.h
, созданный для этого класса с javah
:
#include <jni.h>
#ifndef _Included_ParadoxTestWithJni
#define _Included_ParadoxTestWithJni
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jobject JNICALL Java_ParadoxTestWithJni_createParadox
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
Соответствующая реализация... "где происходит волшебство":
#include "ParadoxTestWithJni.h"
JNIEXPORT jobject JNICALL Java_ParadoxTestWithJni_createParadox
(JNIEnv *env, jclass c)
{
jclass paradoxClass = env->FindClass("Paradox");
jobject paradox = env->AllocObject(paradoxClass);
return paradox;
}
Ответ 2
Скажем, что класс Paradox
находится в пакете test
, как это (конечно, он может быть в пакете по умолчанию, но вопрос его не задает):
package test;
public final class Paradox {
private final Paradox paradox;
public Paradox(Paradox paradox) {
this.paradox = paradox;
if (this.paradox == null) throw new InceptionException();
}
private static class InceptionException extends RuntimeException {
public InceptionException() {
super("Paradox requires an instance of paradox to be instantiated");
}
}
}
Представление XML, если оно было сериализовано XStream, класс будет выглядеть следующим образом:
<test.Paradox/>
Учитывая этот XML, его можно десериализовать без вызова конструктора:
public static void main(String[] args) {
XStream xstream = new XStream(new DomDriver());
Paradox paradoxFromXml = (Paradox) xstream.fromXML("<test.Paradox/>");
System.out.println(paradoxFromXml);
}
Вывод:
[email protected]
В этом ответе используется тот факт, что XStream не вызывает конструктор во время десериализации, как указано в их FAQ:
XStream не вызывает конструктор по умолчанию во время десериализации.
Это, по сути, тот же случай, что и выше. XStream использует тот же механизм, что и сериализация JDK. При использовании расширенного режима с оптимизированным API отражения он не вызывает конструктор по умолчанию. Решением является реализация readResolve или readObject, как показано с последним вопросом.
Итак, используя прямое представление XML XStream по умолчанию, класс может быть инициализирован посредством десериализации, несмотря на то, что класс не сериализуем.
В качестве дополнительного объяснения этому XStream в этом случае, вероятно, инициализирует класс, используя SunLimitedUnsafeReflectionProvider
или SunUnsafeReflectionProvider
, который внутренне использует sun.misc.Unsafe
, поскольку их состояния Javadoc (благодаря marco13 для указав это):
SunLimitedUnsafeReflectionProvider
:
/**
* Instantiates a new object bypassing the constructor using undocumented internal JDK features.
* <p>
* The code in the constructor will never be executed and parameters do not have to be known. This is the same method
* used by the internals of standard Java serialization, but relies on internal code (sun.misc.Unsafe) that may not be
* present on all JVMs.
* <p>
* <p>
* The implementation will use standard Java functionality to write any fields. This requires Java 5 as minimum runtime
* and is used as fallback on platforms that do not provide the complete implementation level for the internals (like
* Dalvik).
* <p>
*
* @author Jörg Schaible
* @author Joe Walnes
* @author Brian Slesinsky
* @since 1.4.7
*/
SunUnsafeReflectionProvider
/**
* Instantiates a new object bypassing the constructor using undocumented internal JDK features.
* <p>
* The code in the constructor will never be executed and parameters do not have to be known. This is the same method
* used by the internals of standard Java serialization, but relies on internal code (sun.misc.Unsafe) that may not be
* present on all JVMs.
* <p>
* <p>
* The implementation will use the same internals to write into fields. This is a lot faster and was additionally the
* only possibility to set final fields prior to Java 5.
* <p>
*
* @author Joe Walnes
* @author Brian Slesinsky
* @author Jörg Schaible
* @since 1.4.7
*/
Честно говоря, я просто помнил, что XStream имел возможности инициализировать объекты без вызова конструкторов, не имея времени в момент ответа на вопрос, чтобы вникнуть в него.