在早期的版本中(Java SE5及以前版本)中只能通过tools.jar中的 com.sun.tools.javac
包来调用Java编译器。
而在Java SE6中为我们提供了标准的包来操作Java编译器,这就是javax.tools
包。
编译Java文件
使用Java API来编译Java源代码有非常多方法,目前让我们来看一种最简单的方法,通过JavaCompiler进行编译。
使用ToolProvider.getSystemJavaCompiler
来得到一个JavaCompiler
接口的实例。
1
| JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
JavaCompiler
中最核心的方法是run()
,通过这个方法能编译java源代码:
1
| int run(InputStream in, OutputStream out, OutputStream err, String... arguments)
|
参数分别用来为:
- java编译器提供参数
- 得到Java编译器的输出信息
- 接收编译器的错误信息,
- 一个或多个Java源程式文件
如果编译成功,则返回0。
如果前3个参数传入的是null
,那么run方法将以标准的输入、输出代替,即System.in
、System.out
和System.err
。
我们先整一个 Test
的 Java 类:
1 2 3 4 5 6 7
| package cn.gs.compiler;
public class Test { public static void main(String[] args) { System.out.println("this is a test.java file ,thank you very much"); } }
|
完整的编译例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public static void main(String[] args) throws IOException { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); int res = compiler.run(null, null, null, "src/cn/gs/compiler/Test.java"); System.out.println(res==0 ? "编译成功" : "编译失败");
Process process = Runtime.getRuntime().exec("java Test.java", null, new File("src/cn/gs/compiler/")); BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); String str = null; while ((str = br.readLine())!=null){ System.out.println(str); } }
|
先看看当前包下的文件:

当我们执行 Demo1
的main方法之后,控制台输出如下:

文件也会多出来一个编译后的 class 文件,如下所示:

动态编译并执行
我们可以对上面的代码稍加改进,使我们的程序在运行过程中也可以动态编译Java文件并执行它们。
我们先写一个写文件的方法,传入一个文件路径的参数,往其中写入Java文件,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public void writeMyDemo(String path){ File file = new File(path); FileOutputStream fos = null; BufferedOutputStream bos = null; StringBuffer sb = new StringBuffer(); sb.append("package cn.gs.compiler;\n"); sb.append("public class MyDemo {\n"); sb.append("public void myMethod(){\n"); sb.append("System.out.println(\"俺的方法执行了...\");\n"); sb.append("}\n"); sb.append("}\n");
try { fos = new FileOutputStream(file); bos = new BufferedOutputStream(fos); bos.write(sb.toString().getBytes()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
主方法中遵循下列步骤:
- 获取当前类加载路径,并写入我们的
MyDemo
类
- 动态编译
MyDemo
类
- 通过反射执行
MyDemo
类的 myMethod
方法
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Test public void Test1() throws Exception { String path = ""; File classPath = new File(Thread.currentThread().getContextClassLoader().getResource("").toURI());
path = classPath.getAbsolutePath() + "/cn/gs/compiler/MyDemo.java";
writeMyDemo(path);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); int res = compiler.run(null, null, null, path); System.out.println(res==0 ? "编译成功" : "编译失败");
if(res==0){ Class<?> aClass = Class.forName("cn.gs.compiler.MyDemo"); Method myMethod = aClass.getMethod("myMethod"); Object o = aClass.getConstructor().newInstance(); myMethod.invoke(o); } }
|
执行结果如下:
