有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

这是本书中的一个问题:https://www.cl.cam.ac.uk/teaching/0506/ConcSys/cs_a-2005.pdf第28页

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, or using library features like reflection, serialization, or native methods are considered cheating! I’ll provide some hints in lectures if nobody can spot it in a week or so. None of the PhD students has got it yet.

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

我知道这个问题很不清楚。我可以想出许多不同的“黑客”解决方案,但不确定它们是否会被视为“作弊”:

我找不到正式答案,所以问你什么是好答案


共 (4) 个答案

  1. # 1 楼答案

    不知怎么的,我不喜欢这种问题。它来自2005年的一次演讲,根据WebSearchs,似乎到目前为止还没有人找到“解决方案”,也没有发表任何解决方案

    约束条件是明确的,但什么是允许的还是不允许的问题有些模糊。每一种解决方案都可能被认为是“作弊”,因为带有私有构造函数的类不应该被子类化。这是一个关键的安全机制,负责任的工程师正在努力确保这个安全机制不能被轻易规避

    当然,为了解决这个问题,你必须作弊

    尽管如此,我还是花了很长一段时间研究这个问题,下面是我最终如何欺骗它的:

    1.)下载Apache Bytecode Engineering Library,并将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类创建了一个扩展AB类,但不调用超级构造函数。(请注意,隐式超级构造函数调用通常由编译器添加。但这里不涉及编译器。字节码是手动创建的)。当类被加载时,所有这些通常都会在VerifyError时失败,但是这个验证可以通过-Xverify:none关闭

    总之:

    • 类本身没有被编辑(它的字节码也没有被编辑,我希望这是清楚的!)
    • 不反思
    • 没有序列化
    • 没有自定义的本机方法
  2. # 2 楼答案

    这里有几个选项:

    创建一个类:

    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规范明确要求验证访问保护

    不过还是很有趣:)

  3. # 3 楼答案

    Java可以支持unicode类名:)

    “if(o instanceof A)”中的A可能不同于“public class 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!");
            }
        }
    }
    

    А。爪哇

    public class А {
        // A: U+0410, not A: U+0041
    }
    

    梅因。爪哇

    public class Main {
        public static void main(String[] args) {
            A.test(new А());
        }
    }
    
  4. # 4 楼答案

    如果我们认为嵌套类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