【Java虛擬機器】Java前端編譯器
阿新 • • 發佈:2018-12-03
Java前端編譯器
關於編譯器
- 前端編譯器:把java檔案轉化為class檔案的過程
- 執行時編譯器(JIT編譯器,Just In Time Compiler):把位元組碼轉化為機器碼的過程
- 靜態提前編譯器(AOT編譯器, Ahead Of Time Compiler):直接把class檔案編譯成機器程式碼的過程。
編譯過程
解析與填充符號表
詞法、語法分析
詞法分析是將原始碼的字元流轉變為標記集合,關鍵字、變數名、字面量、運算子都可以成為標記,如“int a = b + 2”這句程式碼包含了6個標記,分別是int、a、=、b、+、2。
語法分析是根據標記集合構造抽象語法樹的過程,抽象語法樹是一種用來描述程式程式碼語法結構的樹形表示方式。經過這個步驟之後,編譯器基本不會再對原始碼檔案進行操作了,後續的操作都建立在抽象語法樹之上。
填充符號表
符號表示由一組符號地址和符號資訊構成的表格,在編譯的不同階段都要用到。在語義分析中,將用於語義檢查(如檢查一個名字的使用和原先的說明是否一致)和產生中間程式碼。在目的碼生成階段,當對符號名進行地址分配時,符號表示地址分配的依據。
註解處理器
提供了一組插入式註解處理器的標準API在編譯期間對註解進行處理。
可以讀取、修改、新增抽象語法樹中的元素。如果對語法樹進行了修改,將回到解析及填充符號表的過程重新處理,直到不再對語法樹進行修改為止。
語義分析與位元組碼生成
標註檢查
- 檢查變數使用前是否已被宣告
- 變數與賦值之間的資料型別是否能夠匹配
… …
資料及控制流分析
- 程式區域性變數使用前是否有賦值
- 方法的每條路徑是否都有返回值
- 是否所有的受查異常都被正確處理
… …
解語法糖
- 泛型
- 變長引數
- 自動裝箱/拆箱
… …
位元組碼生成
- 例項構造器的生成
- 類構造器的生成
… …
Java語法糖
泛型與型別擦除
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("hello", "你好");
map.put("how are you?", "吃了沒?");
System.out.println(map.get("hello"));
System.out.println(map.get("how are you?"));
}
其實編譯後的是這樣的
public static void main(String[] args) {
Map<String, String> map = new HashMap();
map.put("hello", "你好");
map.put("how are you?", "吃了沒?");
System.out.println((String)map.get("hello"));
System.out.println((String)map.get("how are you?"));
}
由於Java的泛型是擦除的,所有對於執行期的Java語言來說,ArrayList<Integer>
與 ArrayList<String>
就是同一個類
自動裝箱、拆箱與遍歷迴圈
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4);
int sum = 0;
for (int i : list) {
sum += i;
}
System.out.println(sum);
}
其實編譯後的是這樣的
public static void main(String[] args) {
List<Integer> list = Arrays.asList(
new Integer[] { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4) });
int sum = 0;
for (Iterator localIterator = list.iterator(); localIterator.hasNext();) {
int i = ((Integer) localIterator.next()).intValue();
sum += i;
}
System.out.println(sum);
}
遍歷迴圈把程式碼還原成了迭代器的實現,這就是為何遍歷迴圈需要被遍歷的類實現Iterable
介面的原因。
參考
- 深入理解Java虛擬機器[書籍]