悖论实例化

时间:2015-08-31 19:11:14

标签: 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");
        }
    }
}

2 个答案:

答案 0 :(得分:16)

正如评论中已经指出的那样:可以使用<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://maps.googleapis.com/maps/api/js"></script> <div id="map_canvas"></div>类:

sun.misc.Unsafe

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

  

编辑:Someone found another solution - 没有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); } }

     

EDIT2:但请注意,此解决方案还使用了Sun专有类,它们是公共API的一部分,所以此解决方案仍然不可移植并且应该< strong>不用于生产代码!

sun.misc.Unsafe
  

EDIT3:仅为了完整性,并在评论中按要求:基于JNI的解决方案

如上所述,JNI function AllocObject不会调用构造函数,因此这也可用于实例化此类对象。这是包含本机方法并加载本机库的调用类:

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

}

使用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

答案 1 :(得分: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");
        }
    }
}

该类的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);
}

输出结果为:

  

test.Paradox@72d818d1

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

  

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

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

作为对此的进一步解释,在这种情况下,XStream可能使用SunLimitedUnsafeReflectionProviderSunUnsafeReflectionProvider来初始化类,内部使用sun.misc.Unsafe作为其Javadoc状态(感谢{{ 3}}用于指出这一点):

<强> 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
 */

<强> 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
 */

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