问题
一位朋友提出了一个有趣的问题。给出以下代码:
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();
}
}
因此,这个问题的有趣之处在于强调合成方法的使用。使用这些方法,我可以像在解决方案中一样访问私有字段。当然,我需要使用反射,我认为使用这种东西可能非常危险......
问题
作为一名开发人员,我对合成方法有什么兴趣?使用合成物可能有用的好情况是什么?
答案 0 :(得分:1)
正如您所演示的那样,Java访问修饰符仅仅是提供信息,可以通过使用反射来规避它们。所以你的问题大致等同于“规避访问修饰符的兴趣是什么?”除了恶意目的之外,还会想到调试;例如,您可以从库中的外部记录某些库内部的内部状态,而不必完全触及库代码本身。另一个例子是与脚本语言的合作;如果您在编译时不知道哪些类和方法可用,则反射非常有用。例如,Jython的内部使用了大量的反射。
答案 1 :(得分:1)
对于作为开发人员的合成方法,您有什么兴趣?好吧,对于初学者,不要调用它们(因为其他答复者已经解释过的原因)。
但是要记住一件有趣的事情,特别是如果您正在编写Java代码以在安全敏感的环境中运行,那就是合成方法可能会为攻击者提供私有领域的后门。 / p>
当然,有一些很大的“ifs” - 我的意思是必要的条件 - 这样的漏洞实际上很重要:
答案 2 :(得分:0)
您永远不应该使用反射来调用合成访问器方法,它们可能会发生变化,具体取决于您使用的编译器。例如,在我的计算机上运行jdk1.6上的解决方案失败,因为找不到access$000
方法。
合成访问器是一个不为人知的编译器黑客,可以解决内部类是Java 1.1的补充这一事实,并且VM规范从未改变以适应它们。