Javac 源碼位於 langtools/src/share/classes/com/sun/tools/javac,僅依賴 JDK API 與內部 com.sun.* 工具包,調試環境輕量。入口類:com.sun.tools.javac.main.JavaCompiler,核心方法 compile()、compile2()。
| 階段 | 核心處理類 | 輸入 | 輸出 |
|---|---|---|---|
| 1. 解析與填充符號表 | Scanner、Parser、Enter | .java 源碼 | 抽象語法樹(AST)+ 符號表 + 待處理列表 |
| 2. 註解處理 | JavacProcessingEnvironment | AST、符號表 | 可能生成新源碼/類檔,觸發新一輪編譯 |
| 3. 語義分析與字節碼生成 | Attr、Check、Flow、TransTypes、Lower、Gen | AST、符號表 | .class 字節碼檔案 |
com.sun.tools.javac.parser.Scanner。com.sun.tools.javac.parser.Parser,AST 節點由 com.sun.tools.javac.tree.JCTree 表示。com.sun.tools.javac.comp.Enter,輸出 TodoList(每個編譯單元的 AST 頂層節點)。@Override、@Deprecated、Lombok、MapStruct 等)。initProcessAnnotations()、processAnnotations() → JavacProcessingEnvironment.doProcessing()。"a" + "b" → "ab"、3 * 5 → 15),直接嵌入字節碼。Attr(屬性標註)、Check(檢查)。com.sun.tools.javac.comp.Flow。將高階語法「還原」為基礎字節碼指令,由 TransTypes 與 Lower 完成:
| 語法糖 | 原始寫法 | 編譯後等價形式 |
|---|---|---|
| 泛型 | List<String> list = new ArrayList<>(); | 型別擦除為 List list = new ArrayList();,插入強制轉型 checkcast |
| 自動裝箱/拆箱 | Integer i = 10; int n = i; | Integer i = Integer.valueOf(10); int n = i.intValue(); |
| 增強 for 迴圈 | for (String s : list) { ... } | 迭代器模式:for (Iterator i = list.iterator(); i.hasNext(); ) { String s = (String)i.next(); ... } |
| 條件編譯 | if (DEBUG) { log() }(DEBUG 為 static final boolean) | 若 false,整個區塊直接移除,不生成字節碼 |
| try-with-resources | try (Resource r = ...) { ... } | 展開為 try-finally 並呼叫 close() |
| Lambda 表達式 | () -> System.out.println("hi") | 生成 invokedynamic 指令,動態連結至函數式介面實例 |
| Switch 表達式 | switch (x) { case 1 -> ... } | 編譯為表格查詢或 if-else 鏈(Java 12+) |
| 記錄類別 | record Point(int x, int y) {} | 生成 final 欄位、建構子、equals/hashCode/toString、accessor 方法 |
| 特性 | C# (CLR 支援) | Java (擦除實作) |
|---|---|---|
| 執行期型別資訊 | 完整保留(List<int> ≠ List<string>) | 擦除為原始類型(List<String> = List<Integer> = List) |
| 實作機制 | 型別膨脹:執行期動態生成專用類型 | 編譯期擦除 + 插入轉型指令 |
| 基本型別支援 | 直接支援 List<int> | 需裝箱 List<Integer> |
| 效能 | 無裝箱開銷,程式碼膨脹 | 裝箱開銷,單一類型共享程式碼 |
雖執行期擦除,但編譯器在 class 檔案 Signature、LocalVariableTypeTable、MethodParameters 屬性中保留泛型簽名,供反射(getGenericSuperclass())與 IDE 使用。
== 比較陷阱Integer a = 100, b = 100; // [-128,127] 快取範圍內
System.out.println(a == b); // true(同一快取物件)
Integer c = 200, d = 200; // 超出快取範圍
System.out.println(c == d); // false(不同物件)
System.out.println(c.equals(d)); // true(數值相等)
== 不自動拆箱。equals() 不處理型別轉換(Integer.equals(Long) 回傳 false)。for (String s : list) { ... }
// 等價於
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
...
}
注意:迭代過程中不可結構性修改集合(增/刪元素),否則拋 ConcurrentModificationException。
public class ConditionalCompile {
static final boolean DEBUG = false; // 編譯期常量
public static void main(String[] args) {
if (DEBUG) {
System.out.println("Debug info"); // 此區塊字節碼不會生成
}
System.out.println("Release code");
}
}
實現於 Lower 階段:根據布林常量值,直接移除不成立分支的 AST 節點。
static final 常量運算式零執行期開銷。new T()、T[]、instanceof T)。static final boolean + if 實現零開銷特性開關。