悖论实例化

Víc*_*tos 16 java

有没有想过实例化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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*o13 16

正如评论中已经指出的那样:有可能与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);
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,可以使用JNI函数AllocObject,该函数也可以在不调用任何构造函数的情况下分配新对象.

编辑:有人找到了另一种解决方案 - 没有sun.misc.Unsafe!

EDIT2:但是请注意,这个解决方案还采用了Sun的专有类,这是不是公共API的一部分,所以这个解决方案仍然是不可移植的,应该不是在生产代码中使用!

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

}
Run Code Online (Sandbox Code Playgroud)

编辑3:仅为了完整性,并在评论中的每个请求:基于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();
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

相应的实施......"魔术发生的地方":

#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;
}
Run Code Online (Sandbox Code Playgroud)

  • 万一有人想知道**如果你在生产代码中写这个,DAEMONS会撕掉你的手** (17认同)
  • @christopher它会神奇地停止运行Java 9.但守护程序线程不稳定的事情也是可怕的. (4认同)

Mag*_*lex 13

假设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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果已由XStream序列化,则表示类的XML表示如下所示:

<test.Paradox/>
Run Code Online (Sandbox Code Playgroud)

鉴于此XML,可以在不调用构造函数的情况下对其进行反序列化:

public static void main(String[] args) {
    XStream xstream = new XStream(new DomDriver());
    Paradox paradoxFromXml = (Paradox) xstream.fromXML("<test.Paradox/>");
    System.out.println(paradoxFromXml);
}
Run Code Online (Sandbox Code Playgroud)

输出是:

test.Paradox@72d818d1

这个答案充分利用了XStream在反序列化过程中不调用构造函数的事实,如FAQ中所述:

在反序列化期间,XStream不调用默认构造函数.
事实上,这与上述情况相同.XStream使用与JDK序列化相同的机制.使用带有优化反射API的增强模式时,它不会调用默认构造函数.解决方案是实现readResolve或readObject,如上一个问题所示.

因此,通过利用XStream默认的直接XML表示,可以通过反序列化来初始化类,尽管该类不可序列化.

作为对此的进一步解释,在这种情况下,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&ouml;rg Schaible
 * @author Joe Walnes
 * @author Brian Slesinsky
 * @since 1.4.7
 */
Run Code Online (Sandbox Code Playgroud)

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&ouml;rg Schaible
 * @since 1.4.7
 */
Run Code Online (Sandbox Code Playgroud)

老实说,我只记得XStream有能力在没有调用构造函数的情况下初始化对象,而没有时间回答问题以进一步挖掘它.

  • 我认为人们对它的工作方式更感兴趣,而不是看到一些神奇的图书馆做正确的事情 (3认同)