動態編譯是指在程式執行期間,將字串或檔案形式的原始碼編譯為類別檔,並可選擇性載入執行。這在規則引擎、腳本引擎、熱部署等場景極為常見。本文詳細介紹兩種動態編譯方式與兩種動態執行方式的完整實作。
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("javac -cp c:/myjava/ HelloWorld.java");
缺點:啟動新進程開銷大、跨平台相容性差、難以獲取編譯錯誤詳情。
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int result = compiler.run(null, null, null, sourceFile);
參數說明:
回傳值:0 表示編譯成功,非 0 表示失敗。
package com.coscon.compiler;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
public class JavaCompilerTest {
public static void main(String[] args) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int result = compiler.run(null, null, null, "d:/myjava/HelloWorld.java");
System.out.println(result == 0 ? "編譯成功" : "編譯失敗");
}
}
將字串寫入臨時 .java 檔案,再呼叫 JavaCompiler 編譯:
String source = "public class Demo2 { public static void main(String[] args) { System.out.print(\"編譯字串\"); } }";
// 1. 寫入臨時檔案
// 2. 呼叫 compiler.run() 編譯
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("java -cp c:/myjava/ HelloWorld");
// 獲取輸出
InputStream in = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
URL[] urls = new URL[]{new URL("file:/d:/myjava/")};
URLClassLoader loader = new URLClassLoader(urls);
Class<?> clazz = loader.loadClass("HelloWorld");
// 呼叫 main 方法
Method main = clazz.getMethod("main", String[].class);
main.invoke(null, (Object) new String[]{}); // 靜態方法第一個參數傳 null
優勢:無需啟動新進程、直接在當前 JVM 執行、效能高、易於整合。
JavaCompiler API 配合 URLClassLoader 反射載入,是現代 Java 動態編譯執行的標準方案。相比 Runtime.exec(),它提供更好的效能、錯誤處理與整合性。實務應用中,建議封裝為通用工具類,並嚴格控制輸入原始碼來源以確保安全。