有没有想过实例化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)
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ö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örg Schaible
* @since 1.4.7
*/
Run Code Online (Sandbox Code Playgroud)
老实说,我只记得XStream有能力在没有调用构造函数的情况下初始化对象,而没有时间回答问题以进一步挖掘它.