合成方法有什么好处?

时间:2010-03-17 07:30:47

标签: java reflection inner-classes java-synthetic-methods

问题

一位朋友提出了一个有趣的问题。给出以下代码:

public class OuterClass {

    private String message = "Hello World";

    private class InnerClass {
        private String getMessage() {
            return message;
        }
    }

}

从外部课程中,我如何打印message变量内容?当然,更改方法或字段的可访问性是允许。

(来源here,但它是法国博客)


解决方案

解决此问题的代码如下:

try {
    Method m = OuterClass.class.getDeclaredMethod("access$000", OuterClass.class);
    OuterClass outerClass = new OuterClass();
    System.out.println(m.invoke(outerClass, outerClass));
} catch (Exception e) {
    e.printStackTrace();
}

请注意,access$000方法名称不是标准名称(即使此格式是strongly recommanded的格式),并且某些JVM会将此方法命名为access$0。因此,更好的解决方案是检查合成方法:

Method method = null;
int i = 0;
while ((method == null) && (i < OuterClass.class.getDeclaredMethods().length)) {
    if (OuterClass.class.getDeclaredMethods()[i].isSynthetic()) {
        method = OuterClass.class.getDeclaredMethods()[i];
    }
    i++;
}
if (method != null) {
    try {
        System.out.println(method.invoke(null, new OuterClass()));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

因此,这个问题的有趣之处在于强调合成方法的使用。使用这些方法,我可以像在解决方案中一样访问私有字段。当然,我需要使用反射,我认为使用这种东西可能非常危险......

问题

作为一名开发人员,我对合成方法有什么兴趣?使用合成物可能有用的好情况是什么?

3 个答案:

答案 0 :(得分:1)

正如您所演示的那样,Java访问修饰符仅仅是提供信息,可以通过使用反射来规避它们。所以你的问题大致等同于“规避访问修饰符的兴趣是什么?”除了恶意目的之外,还会想到调试;例如,您可以从库中的外部记录某些库内部的内部状态,而不必完全触及库代码本身。另一个例子是与脚本语言的合作;如果您在编译时不知道哪些类和方法可用,则反射非常有用。例如,Jython的内部使用了大量的反射。

答案 1 :(得分:1)

对于作为开发人员的合成方法,您有什么兴趣?好吧,对于初学者,不要调用它们(因为其他答复者已经解释过的原因)。

但是要记住一件有趣的事情,特别是如果您正在编写Java代码以在安全敏感的环境中运行,那就是合成方法可能会为攻击者提供私有领域的后门。 / p>

当然,有一些很大的“ifs” - 我的意思是必要的条件 - 这样的漏洞实际上很重要:

  1. 攻击者实际上必须在JVM中运行代码。大多数情况下这不是问题,因为唯一运行的代码是您自己的代码。
  2. 攻击者的代码需要与您的内部类在同一个包中(因为合成方法被声明为package-private)。
  3. 攻击者的代码必须从与您的类相同的类加载器加载。如果您让不受信任的代码在JVM中运行(条件#1),那么您最好为不受信任的代码使用单独的类加载器。

答案 2 :(得分:0)

您永远不应该使用反射来调用合成访问器方法,它们可能会发生变化,具体取决于您使用的编译器。例如,在我的计算机上运行jdk1.6上的解决方案失败,因为找不到access$000方法。

合成访问器是一个不为人知的编译器黑客,可以解决内部类是Java 1.1的补充这一事实,并且VM规范从未改变以适应它们。