【问题标题】:What are the interets of synthetic methods?合成方法有什么好处?
【发布时间】:2010-03-17 07:30:47
【问题描述】:

问题

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

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

所以这个问题的有趣之处在于突出了合成方法的使用。使用这些方法,我可以像在解决方案中那样访问私有字段。当然,我需要使用反射,而且我认为使用这种东西可能会很危险......

问题

对于作为开发人员的我来说,合成方法的兴趣是什么?什么情况下可以使用合成物?

【问题讨论】:

  • 合成方法不是由 JVM 生成的。它们由 javac/scalac/fsc/groovyc/... 生成

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


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    作为开发人员,您对合成方法有何兴趣?好吧,对于初学者来说,不要调用它们(出于其他回答者解释的原因)。

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

    当然,有一些重要的“如果”——我的意思是,必要条件——让这样的漏洞真正重要:

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

    【讨论】:

      【解决方案3】:

      您永远不应该使用反射调用合成访问器方法,它们可能会根据您使用的编译器而改变。例如,在我的机器上在 jdk1.6 上运行您的解决方案失败,因为找不到 access$000 方法。

      合成访问器是一种隐蔽的编译器破解方法,用于解决内部类是 Java 1.1 的补充这一事实,并且 VM 规范从未更改以适应它们。

      【讨论】:

      • 是的,我知道更改私有字段的可访问性是一个“更好”的解决方案。但是,正如问题中所述,不允许更改方法或字段的可访问性
      • @romaintaz:对不起,我误解了你,我以为你的意思是改变访问修饰符。我已经删除了答案的一部分,其余的仍然存在。
      猜你喜欢
      • 2013-05-15
      • 2019-12-14
      • 2011-01-27
      • 1970-01-01
      • 2014-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多