看JAVA的反射时,看到有个synthetic ,还有一个方法isSynthetic() 很好奇,就了解了一下:html
Any constructs introduced by a Java compiler that do not have a corresponding construct in the source code must be marked as synthetic, except for default constructors, the class initialization method, and the values and valueOf methods of the Enum class.java
大意为:由java编译器生成的(除了像默认构造函数这一类的)方法,或者类程序员
既然知道synthetic方法和synthetic类是由编译器生成的,那到底编译器会怎么生成这些东西,又在什么状况下会生成这些东西呢?数组
先看一段代码:ide
import static java.lang.System.out; public final class DemonstrateSyntheticMethods { public static void main(final String[] arguments) { DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass(); out.println("String: " + nested.highlyConfidential); } private static final class NestedClass { private String highlyConfidential = "Don't tell anyone about me"; private int highlyConfidentialInt = 42; private Calendar highlyConfidentialCalendar = Calendar.getInstance(); private boolean highlyConfidentialBoolean = true; } }
编译以后,能够看到三个文件:函数
其中,最下面的这个类文件很好解释,就是咱们的主class,中间的文件,是咱们的内部类,上面的文件,后面再讲,咱们先看一下中间这个内部类spa
用javap 反编译DemonstrateSyntheticMethods$NestedClass.class,获得以下结果:3d
javap DemonstrateSyntheticMethods\$NestedClass.class Compiled from "DemonstrateSyntheticMethods.java" final class DemonstrateSyntheticMethods$NestedClass { DemonstrateSyntheticMethods$NestedClass(DemonstrateSyntheticMethods$1);
static java.lang.String access$100(DemonstrateSyntheticMethods$NestedClass); }
先把构造函数放一边,咱们来看这个标黑的方法access$100 这个是怎么回事呢?咱们的源文件里找不到这个access方法啊?code
这个方法就是编译器生成的synthetic方法,读者不信的话,能够用method.isSynthetic() 去验证一下。htm
为什么要生成这样一个方法呢?
能够看到,咱们的NestedClass类中,highConfidential是一个私有属性,而咱们在外部类DemonstrateSyntheticMethods中,直接引用了这个属性。做为一个内部类,NestedClass的属性被外部类引用,在语义上毫无问题,可是这却苦了编译器。
为了能让一个private的变量被引用到,编译器生成了一个package scope的access方法,这个方法就是一个get方法,在外部类使用highConfidential这个属性时,实际是使用了这个access方法。
在javap中能够看到直接的证据:
图中红框的位置,能够很清楚的看到main方法实际上调用了access$100这个方法。
因此,结论很清楚了,编译器为了方便内部类的私有成员被外部类引用,生成了一个get方法,这能够被理解为一个trick,绕开了private成员变量的限制。
定义已经提到,编译器不单单会生成方法,也会生成synthetic类。
咱们回过头来看2.1提到的最后一个类DemonstrateSyntheticMethods$1.class
这个类是一个彻底的空类,反编译后是这个样子:
// $FF: synthetic class class DemonstrateSyntheticMethods$1 { }
这个类只出场了一次,做为内部类NestedClass的package scope的构造函数,如图所示:
那么,这个类的做用呢?笔者查了不少资料,都没有明确的说明这个类的用途,只能根据代码作推测以下:
NestedClass做为一个private类,其默认构造函数也是private的。那么,事实上,做为外部类的DemonstrateSyntheticMethods类,没有办法new这个内部类的对象,而这和咱们须要的语义相违背。
那么,为了实现语义,编译器又用了一个trick,悄悄的生成了一个构造函数NestedClass(DemonstrateSyntheticMethods$1 obj), 这个构造函数是包可见的。
那么,外部类则能够经过new NestedClass(null)的方式,获得内部类的对象。若是读者检查一下main方法的话,能够看到这个方法的调用以下图所示。
这就是这个synthetic类的做用。若是咱们给咱们的NestedClass 增长一个public级别的默认构造函数的话,则能够看到编译器不会再生成这个synthetic类。
编译器经过生成一些在源代码中不存在的synthetic方法和类的方式,实现了对private级别的字段和类的访问,从而绕开了语言限制,这能够算是一种trick。
在实际生产和应用中,基本不存在程序员须要考虑synthetic的地方。
PS: 在此提一个的常见的存在synthetic的案例。
若是同时用到了Enum和switch,如先定义一个enum枚举,而后用switch遍历这个枚举,java编译器会偷偷生成一个synthetic的数组,数组内容是enum的实例。
对于这种状况,笔者找到了一个资料,可供参考:
完。