代码之家  ›  专栏  ›  技术社区  ›  Romain Linsolas

合成方法的内在联系是什么?

  •  2
  • Romain Linsolas  · 技术社区  · 14 年前

    问题

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

    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 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 回复  |  直到 11 年前
        1
  •  1
  •   Joonas Pulakka    14 年前

    正如您所演示的,Java访问修饰符只是提供信息,可以通过使用反射来规避它们。所以你的问题大致相当于“绕过访问修饰符有什么好处?”除了恶意的目的, 在脑海中浮现;例如,您可以从 外部 库,而不必触及库代码本身。另一个例子是 与脚本语言的协作 ; 如果在编译时不知道哪些类和方法是可用的,那么反射是非常有用的。例如, Jython

        2
  •  1
  •   Will    14 年前

    作为开发人员,您对合成方法的兴趣是什么?首先,不要调用它们(因为其他回答者已经解释过的原因)。

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

    当然,有一些大的“如果”--我的意思是,必要的条件--对于这样一个脆弱性实际上是重要的:

    1. 攻击者实际上必须在JVM中运行代码。大多数情况下,这不是问题,因为运行的唯一代码是您自己的。
    2. 攻击者的代码必须从与您的类相同的类加载器加载。如果让不受信任的代码在JVM中运行(条件#1),那么最好对不受信任的代码使用单独的类加载器。
        3
  •  0
  •   skaffman    14 年前

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

    合成访问器是一种隐藏的编译器黑客,它绕过了这样一个事实:内部类是Java1.1的一个补充,VM规范从未更改以适应它们。