[英]How can instanceof return true for a class that has a private constructor?
这是本书中的一个问题: https : //www.cl.cam.ac.uk/teaching/0506/ConcSys/cs_a-2005.pdf第28页
你能编写一个额外的Java类来创建一个对象,当传递给测试方法时会导致它打印“Here!”吗? 正如我在代码中所说,编辑A类本身,或使用反射,序列化或本机方法等库功能被视为作弊! 如果没有人能在一周左右发现它,我会在讲座中提供一些提示。 博士生都没有得到它。
public class A {
// Private constructor tries to prevent A
// from being instantiated outside this
// class definition
//
// Using reflection is cheating :-)
private A() {
}
// ’test’ method checks whether the caller has
// been able to create an instance of the ’A’
// class. Can this be done even though the
// constructor is private?
public static void test(Object o) {
if (o instanceof A) {
System.out.println("Here!");
}
}
}
我知道这个问题很不清楚。 我可以想到许多不同的'hack-ish'解决方案,但不确定它们是否会被视为'作弊'或者不是:)
我找不到官方的答案,所以问你一个好的答案。
如果我们认为嵌套类A不“修改它”(从技术上讲,所有代码行都是完整的)那么这个解决方案可能是唯一有效的选项:
class B
{
static
public class A {
// Private constructor tries to prevent A
// from being instantiated outside this
// class definition
//
// Using reflection is cheating :-)
private A() {
}
// ’test’ method checks whether the caller has
// been able to create an instance of the ’A’
// class. Can this be done even though the
// constructor is private?
public static void test(Object o) {
if (o instanceof A) {
System.out.println("Here!");
}
}
}
public static void main (String[] args) throws java.lang.Exception
{
A.test(new A());
}
}
我的意思是,从技术上讲,它遵循所有规则:
Can you write an additional Java class which creates an object that, when passed to the test method causes it to print “Here!”?
- 完成 As I say in the code, editing the class A itself ... considered cheating!
- 从技术上讲 ,这门课程是未经编辑的。 我复制粘贴到我的代码中。 ... or using library features like reflection, serialization, or native methods are considered cheating!
- 完成 但是,如果你决定不允许嵌套类A
,那么我认为在给定当前定义的情况下,没有适当的解决方案。 另外,鉴于本书的这一部分是给出的,我打赌作者希望使构造函数protected
但不是private
。
不知何故,我不喜欢这类问题。 它来自于2005年的一次演讲,根据网络搜索,似乎到目前为止还没有人找到“解决方案”,也没有发布任何解决方案。
约束是明确的,但是允许与否的问题有些模糊。 每种解决方案都可以被视为“作弊”,以一种或另一种方式,因为具有私有构造函数的类不应该被子类化 。 这是一个关键的安全机制,负责的工程师正在努力确保这种安全机制不会被轻易绕过。
所以当然,你必须作弊才能解决这个问题。
尽管如此,我花了很长一段时间用这个,这就是我最终欺骗它的方式:
1.)下载Apache字节码工程库 ,并将bcel-6.0.jar
放在一个目录中。
2.)在同一目录中创建一个CreateB.java
文件,其中包含以下内容:
import java.io.FileOutputStream;
import org.apache.bcel.Const;
import org.apache.bcel.generic.*;
public class CreateB
{
public static void main(String[] args) throws Exception
{
ClassGen cg = new ClassGen("B", "A", "B.java",
Const.ACC_PUBLIC | Const.ACC_SUPER, new String[] {});
ConstantPoolGen cp = cg.getConstantPool();
InstructionList il = new InstructionList();
MethodGen method = new MethodGen(Const.ACC_PUBLIC, Type.VOID,
Type.NO_ARGS, new String[] {}, "<init>", "B", il, cp);
il.append(InstructionFactory.createReturn(Type.VOID));
method.setMaxStack();
method.setMaxLocals();
cg.addMethod(method.getMethod());
il.dispose();
cg.getJavaClass().dump(new FileOutputStream("B.class"));
}
}
3.)编译并执行此类:
javac -cp .;bcel-6.0.jar CreateB.java
java -cp .;bcel-6.0.jar CreateB
(注:在Linux中, ;
必须是:
)。 结果将是一个文件B.class
。
4.)将问题中给出的类(逐字 - 未经任何修改)复制到同一目录中并进行编译。
5.)在同一目录中创建以下类,并编译它:
public class TestA
{
public static void main(String[] args)
{
A.test(new B());
}
}
6.)关键步骤:致电
java -Xverify:none TestA
输出将在Here!
。
关键是CreateB
类创建一个扩展A
的类B
,但不调用超级构造函数。 (请注意,编译器通常会添加隐式超级构造函数调用。但此处不涉及编译器。字节码是手动创建的)。 加载类时,所有这些通常都会因VerifyError
失败,但可以使用-Xverify:none
关闭此验证。
总结如下:
这里有几个选项:
创建一个类:
public class Y extends A {
public static void main(String[] args) throws Exception {
X.test(new Y());
}
}
然后编辑字节码并删除对X的调用。当然这违反了JVM规范,必须使用-Xverify:none
运行,如上所述。 这与@ Marco13基本相同。
选项2:
import sun.misc.Unsafe;
public class Y extends A {
public static void main(String[] args) throws Exception {
Unsafe uf = Unsafe.getUnsafe();
X.test((X) uf.allocateInstance(X.class));
}
}
编译代码并通过将类路径放在sysloader中来运行它(否则它将无法工作):
$ java -Xbootclasspath/p:. Y
两者都适合我:)当然,他们都是作弊。 第一个选项不是Java。 第二个是,好吧,邪恶:)
如果我找到另一种方式,我会发布它:)
无论如何,如果没有低级别的技巧,这是不可能做到的。 JVM规范明确禁止在不调用构造函数的情况下创建对象,因为堆栈中的对象未初始化 。 并且JVM规范明确禁止不调用超级构造函数。 JVM规范明确要求验证访问保护。
尽管很有趣,但:)
Java可以支持unicode类名:)
“if(o instanceof A)”中的A可能与“公共A类”中的A不同
例如,下面的代码将打印“Here!” 而不是“坏”。
A.java
public class A {
// Private constructor tries to prevent A
// from being instantiated outside this
// class definition
//
// Using reflection is cheating :-)
private A() {
// A: U+0041
}
// ’test’ method checks whether the caller has
// been able to create an instance of the ’A’
// class. Can this be done even though the
// constructor is private?
public static void test(Object o) {
if (o instanceof А) {
System.out.println("Here!");
}
}
}
А.java
public class А {
// A: U+0410, not A: U+0041
}
Main.java
public class Main {
public static void main(String[] args) {
A.test(new А());
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.