首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 動態編譯與執行:RuntimeCode 完整實作

     編輯:Java 相關知識

前言

Java 透過 com.sun.tools.javac.Main 可在執行期動態編譯原始碼並透過反射載入執行。本文展示一個完整的 RuntimeCode 類別,可從標準輸入讀取 Java 程式碼片段,自動包裝成類別、編譯、載入並呼叫 main()。

完整原始碼

import java.io.*;
import java.lang.reflect.Method;
import com.sun.tools.javac.Main;

public class RuntimeCode {
    private static Main javac = new Main();

    public static void main(String[] args) throws Exception {
        String code = "";
        DataInputStream bd = new DataInputStream(System.in);
        byte[] brray = new byte[200];
        int i = bd.read(brray);
        code = new String(brray, 0, i);
        run(compile(code));
        System.out.print(code);
    }

    private synchronized static File compile(String code) throws Exception {
        File file = File.createTempFile("JavaRuntime", ".java",
            new File(System.getProperty("user.dir")));
        System.out.println(System.getProperty("user.dir"));

        String filename = file.getName();
        String classname = getClassName(filename);

        PrintWriter out = new PrintWriter(new FileOutputStream(file));
        out.write("class " + classname + "{" +
                  "public static void main(String[] args)" +
                  "{");
        out.write(code);
        out.write("}}");
        out.flush();
        out.close();

        String[] args = new String[] { "-d",
            System.getProperty("user.dir"), filename };
        int status = javac.compile(args);
        System.out.println(status);
        return file;
    }

    private static synchronized void run(File file) throws Exception {
        String filename = file.getName();
        String classname = getClassName(filename);
        new File(file.getParent(), classname + ".class").deleteOnExit();

        try {
            Class cls = Class.forName(classname);
            Method main = cls.getMethod("main", new Class[] { String[].class });
            main.invoke(null, new Object[] { new String[0] });
        } catch (SecurityException se) {
            // 忽略安全例外
        }
    }

    private static void debug(String msg) {
        System.err.println(msg);
    }

    private static String getClassName(String filename) {
        return filename.substring(0, filename.length() - 5); // 移除 .java
    }
}

運作流程

  1. 從 System.in 讀取最多 200 位元組的 Java 程式碼字串。
  2. compile():
    • 在目前工作目錄建立暫存 .java 檔(前綴 JavaRuntime)。
    • 將使用者程式碼包裝在 class X { public static void main(...) { ... } } 中。
    • 呼叫 com.sun.tools.javac.Main.compile() 編譯,-d 指定輸出目錄為工作目錄。
    • 回傳暫存檔 File 物件。
  3. run():
    • 註冊 JVM 結束時刪除產生的 .class 檔。
    • 用 Class.forName() 載入類別。
    • 反射取得 main(String[]) 方法並 invoke() 執行。

注意事項與限制

  • 依賴 com.sun.tools.javac.Main(Sun/Oracle JDK 內部 API),非標準 API,不同 JDK 實作可能不可用(如 OpenJDK 需額外模組)。
  • 輸入緩衝區固定 200 位元組,實務上應用 BufferedReader 讀取完整輸入。
  • 僅支援單一 main() 方法的程式碼片段,非完整類別定義。
  • 安全性:動態編譯執行任意程式碼風險極高,生產環境嚴禁使用,僅供教學/測試。
  • Java 9+ 模組化後,需添加 --add-modules jdk.compiler 或使用標準 javax.tools.JavaCompiler API(建議做法)。

現代替代方案:javax.tools.JavaCompiler

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null);
// ... 使用 JavaFileObject 傳入字串原始碼編譯

此為標準 API,不依賴內部類別,相容性更佳。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved