Java SE 6 引入標準化的 javax.tools 包,徹底改變了以往依賴 tools.jar 內部 API 的尷尬局面。本文系統講解三種調用編譯器的方式,並演示記憶體中動態編譯執行的進階技巧。
適用於簡單編譯場景,核心類 JavaCompiler、ToolProvider:
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 參數:標準輸入、標準輸出、錯誤輸出、源文件路徑(可變參數)
int result = compiler.run(null, null, null, "Test.java");
System.out.println(result == 0 ? "編譯成功" : "編譯失敗");
特點:零配置、直接使用系統預設輸出流、返回碼判斷結果。
提供診斷監聽、編碼控制、輸出目錄自定義等企業級功能:
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector diagnostics = new DiagnosticCollector<>();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
// 準備編譯單元
Iterable extends JavaFileObject> compilationUnits =
fileManager.getJavaFileObjectsFromStrings(Arrays.asList("Test.java"));
// 編譯選項:-d 指定輸出目錄,-sourcepath 指定依賴源碼路徑
Iterable options = Arrays.asList("-d", "D:/output");
CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits);
boolean success = task.call();
fileManager.close();
// 詳細錯誤診斷
for (Diagnostic> d : diagnostics.getDiagnostics()) {
System.out.printf("行號:%d 錯誤:%s%n", d.getLineNumber(), d.getMessage(Locale.getDefault()));
}
無需磁盤 I/O,源碼以字串形式傳入,編譯後直接反射調用:
// 自定義 JavaFileObject 承載字串源碼
class StringSource extends SimpleJavaFileObject {
private final String code;
StringSource(String name, String code) {
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
@Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { return code; }
}
// 使用示例
StringWriter writer = new StringWriter();
PrintWriter out = new PrintWriter(writer);
out.println("public class HelloWorld {");
out.println(" public static void main(String[] args) {");
out.println(" System.out.println(\"Hello, Dynamic Java!\");");
out.println(" }");
out.println("}");
out.close();
JavaFileObject source = new StringSource("HelloWorld", writer.toString());
CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, Arrays.asList(source));
if (task.call()) {
Class> clazz = Class.forName("HelloWorld");
clazz.getMethod("main", String[].class).invoke(null, (Object) new String[0]);
}
| 選項 | 用途 |
|---|---|
-d <dir> | 指定 .class 輸出目錄 |
-sourcepath <path> | 指定依賴源碼搜尋路徑 |
-encoding <enc> | 指定源文件編碼(如 UTF-8) |
-processor <class> | 指定註解處理器 |