首先,部分總結文字引用 簡書做者:Eric新之助 。連接:https://www.jianshu.com/p/4de08deb6ba4
已得到受權java
先簡單瞭解下定義程序員
語法糖(Syntactic Sugar),也叫糖衣語法,是英國計算機科學家彼得·約翰·蘭達(Peter J. Landin)發明的一個術語。指的是,在計算機語言中添加某種語法,這種語法能使程序員更方便的使用語言開發程序,同時加強程序代碼的可讀性,避免出錯的機會。
幾乎每種語言都提供語法糖,它只是編譯器實現的一些小把戲罷了,編譯期間以特定的字節碼或者特定的方式對這些語法作一些處理,開發者就能夠直接方便地使用了。這些語法糖雖然不會提供實質性的功能改進,可是它們或能提升性能、或能提高語法的嚴謹性、或能減小編碼出錯的機會。Java提供給了用戶大量的語法糖,好比泛型、自動裝箱/拆箱、foreach循環、變長參數、內部類、枚舉類、斷言、JAVA8新特性(lambda、stream、方法引用等)......spring
語法糖的存在主要是方便開發人員使用。但其實,Java 虛擬機並不支持這些語法糖,這些語法糖在編譯階段就會被還原成簡單的基礎語法結構,這個過程就是解語法糖。
說到編譯,你們確定都知道,Java 語言中,javac命令能夠將後綴名爲.java的源文件編譯爲後綴名爲.class的能夠運行於 Java 虛擬機的字節碼。
若是你去看com.sun.tools.javac.main.JavaCompiler的源碼,你會發如今compile()中有一個步驟就是調用desugar(),這個方法就是負責解語法糖的實現的。數組
學習語法糖原理最好的辦法就是反編譯看源碼~springboot
反編譯工具:函數
IDEA默認反編譯內置插件: JD-IntelliJ 工具
對java8支持良好的反編譯工具: procyon-decompiler性能
使用方法 : java -jar (jar包路徑)\procyon-decompiler-0.5.30.jar(class文件路徑)*.class
只支持到jdk1.5的反編譯工具: jad學習
使用方法 : jad -o -8 -r -d(輸出反編譯文件路徑) -sjava (class文件路徑)
可變參數由數組實現
Ps:可變長度參數必須做爲方法參數列表中的的最後一個參數且方法參數列表中只能有一個可變長度參數編碼
對於數組,foreach是用普通for循環實現的。
說明在對有實現Iterable接口的對象採用foreach語法糖的話,編譯器會將這個for關鍵字轉化爲對目標的迭代器使用。
因此若是想要本身自定義的類能夠採用foreach語法糖就要實現Iterable接口了。
能夠看到在自動裝箱的時候,Java虛擬機會自動調用Integer的valueOf方法;
在自動拆箱的時候,Java虛擬機會自動調用Integer的intValue方法。這就是自動拆箱和自動裝箱的原理
代碼:
IDEA反編譯:
procyon-decompiler反編譯:
jad反編譯:
對於java虛擬機來講,他根本不認識Map<Integer, String> map這樣的語法。須要在編譯階段經過類型擦除的方式進行解語法糖。
類型擦除的主要過程以下:
將全部的泛型參數用其最左邊界(最頂級的父類型)類型替換。 移除全部的類型參數。
代碼:
IDEA反編譯:
procyon-decompiler反編譯:
jad反編譯:
泛型編譯出來的代碼是會把類型擦除的,因此以下的代碼是不能編譯的,是由於參數List<Integer>和List<String>編譯以後都被擦除了,變成了同樣的原生類型List<E>,擦除動做致使這兩個方法的特徵簽名變得如出一轍,或者說兩個如出一轍的方法不能共存在一個class文件裏
那麼若是加上返回類型呢?
上面這段代碼,IDE沒法編譯經過,javac編譯能夠經過。
網上找到一段引用:
在《Java虛擬機規範第二版》(JDK 1.5修改後的版本)的「§4.4.4
Signatures」章節及《Java語言規範第三版》的「§8.4.2 Method
Signature」章節中分別都定義了字節碼層面的方法特徵簽名,以及Java代碼層面的方法特徵簽名,特徵簽名最重要的任務就是做爲方法獨一無二不可重複的ID,在Java代碼中的方法特徵簽名只包括了方法名稱、參數順序及參數類型,而在字節碼中的特徵簽名還包括方法返回值及受查異常表。
根據上面的例子說明:因爲List<String>和List<Integer>擦除後是同一個類型,只能添加兩個並不須要實際使用到的返回值才能完成重載。這是不是一種引入泛型後的折中的解決方案呢?
Java枚舉編譯後其實是生成了一個類,該類繼承了 java.lang.Enum<E>,並添加了一個返回枚舉數組的values()方法和valueOf()方法。
代碼:
IDEA反編譯:
procyon-decompiler反編譯:
jad反編譯:
Java的內部類也是一個語法糖,它僅僅是一個編譯時的概念,outer.java裏面定義了一個內部類inner,一旦編譯成功,就會生成兩個徹底不一樣的.class文件了,分別是outer.class和outer$inner.class。因此內部類的名字徹底能夠和它的外部類名字相同。
代碼:
IDEA反編譯:
procyon-decompiler反編譯:
jad反編譯:
Parsing /Users/dasouche/Downloads/product/springboot-demo/target/classes/com/example/demo/DemoOutClass.class...Parsing inner class /Users/dasouche/Downloads/product/springboot-demo/target/classes/com/example/demo/DemoOutClass$InnerClass.class... Generating /Users/dasouche/Desktop/jad158g.mac.intel/com/example/demo/DemoOutClass.java
代碼:
IDE反編譯:
procyon-decompiler反編譯:
jad反編譯:
Lambda表達式在Java 8中首先會生成一個私有的靜態函數,這個私有的靜態函數乾的就是Lambda表達式裏面的內容
代碼:
IDEA反編譯:
procyon-decompiler反編譯:
jad反編譯 報錯:
用javap反編譯後: