隐秘而诡异的Java合成方法
Java程序里其实有很多我们看不到的代码,这些代码由Java编译器在编译过程中生成帮助程序更准确地运行。本文就来深入了解一下由编译器加入到Java代码中的方法(Method),特别是合成方法(Synthetic Method)。
合成方法
合成成员(Synthetic Member)在JVM细则里可以找到简单的定义
A class member that does not appear in the source code must be marked using a Synthetic attribute, or else it must have its ACC_SYNTHETIC flag set.
合成成员包含有合成类(Synthetic Class),合成变量(Synthetic Variable),合成方法(Synthetic Method),本文主要讨论合成方法。
合成方法不出现在.java文件中,不过编译之后会在.class里出现,并可以通过以下几个方法发现它们:
- 在设置断点进行debug时会在方法栈上看到这些由编译器加入的方法
- 通过反射(Reflection)查找到方法,通过isSyntehtic()方法来判定是否是合成函数
- 利用一些反编译工具,比如 javap、JD-GUI、jad,查看.class文件可以找到这些方法
对于一般的Java程序而言并没有什么大的问题,只不过是额外的方法调用,代价比较低。但是像Android,它的Dex文件对于Java的方法有一定的约束,就值得去考虑如何避免额外的方法被生成。下边就来介绍一些常见的会出现合成方法的情况让我们有清楚地了解Java的运行机制。
嵌套类和私有成员
先来看一个代码案例,代码中定义了一个顶层类和嵌套类,互相会方法对方的一些private修饰的方法和字段。
[codesyntax lang=”java” lines=”normal”]
public class Outer { private int privateField = 21; private Inner inner = new Inner(); private int privateMethod() { return inner.privateMethod(); } private void print() { inner.print(); } private static int privateStaticMethod() { return 7; } class Inner { private int privateMethod() { return privateField; } private void print() { System.out.println(Outer.this.privateMethod()); System.out.println(privateStaticMethod()); } } public static void main(String[] args) { Outer outer = new Outer(); outer.print(); } }
[/codesyntax]
之前的文章有介绍了Java的修饰访问符的限制范围,类中由private修饰的成员,只能被该类自身所访问,因此嵌套类的私有成员不应该能被外部类访问。另一方面嵌套类虽然在外部类里面,但其实并不属于外部类的一部分,比如编译上边的代码会得到两个.class文件,Inner类被编译成了package可见的独立类。