看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的實例。
對於這種狀況,筆者找到了一個資料,可供參考:
完。