繁体   English   中英

对于具有私有构造函数的类,instanceof如何返回true?

[英]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());
    }
}

我的意思是,从技术上讲,它遵循所有规则:

  1. Can you write an additional Java class which creates an object that, when passed to the test method causes it to print “Here!”? - 完成
  2. As I say in the code, editing the class A itself ... considered cheating! - 从技术上讲 ,这门课程是未经编辑的。 我复制粘贴到我的代码中。
  3. ... 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关闭此验证。

总结如下:

  • A类本身没有编辑(也没有编辑它的字节代码,我希望这很清楚!)
  • 没有反思
  • 没有序列化
  • 没有自定义本机方法

这里有几个选项:

创建一个类:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM