在说动态编译之前先说说静态编译,静态编译就是你在编译程序生成.exe文件时编译器会将所有的模块加载进去,这样一来只要一启动文件,那么所有的模块都会被加载,小的一些程序不存在什么大的问题,但如果程序过大,这样就会让无用模块占用系统资源。而动态编译是先将你所有的模块编写成DDL(dynamic link library),DDL我们后边专门再讲,编写成DLL之后,程序在运行过程中用到哪个模块就加载哪个模块,不会加载无用的模块占用系统资源。
DDL:指的是动态链接库,存在格式为.ddl,该文件中一般存在着一些可以供不同程序都可以使用的模块,所有动态编译的可执行性文件都带有DDL,如果没有那么这个程序就不能正常运行了。
这里我再举一个例子,形象的来比喻一下动态编译和静态编译的区别:
静态编译好比学校要举办一场篮球比赛,你是负责场地的,他们只告诉你去某某篮球场预定一个他们常用的场地,你到达了篮球场但是发现有三个场地,这个时候你也不知道他们常用的哪个,所以你就全部占了下来,然而他们真正使用的时候也是只是用一个场地,这样就导致了资源的浪费,那么动态编译就是说,你们要A场地我就占A场地,要B就占B,这样就不会影响其他场地的正常使用了。
动态编译
动态编译的两种做法:
(1)、通过Runtime调用Javac,启动新的进程去操作。
Runtime run=Runtime.getRuntime();
Process process=run.exec("javac -cp d:/myjava/ HelloWorld.java");
(2)、通过JavaCompiler动态编译。
这里我使用第二种编译。
代码如下:
public static int compileFile(String sourceFile) { JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); int result=compiler.run(null,null,null,sourceFile); System.out.println(result==0?"编译成功":"编译失败"); return result; }
第一个参数:为Java编译器提供一个参数,是InputStream,如果为null则默认是控制台输入system.in
第二个参数:是OutputStream,可以获得编译器的输出信息,如果为null则默认是控制台输出system.out
第三个参数:也是一个输出流,接受编译器输出的一些错误信息,如果为null则默认是把错误消息输出在控制台上 system.error
第四个参数:可以传递多个Java源文件地址。
返回值:0表示成功,其他数表示失败
第一次使用时报错为空指针异常,检查了一遍代码发现并没有错误,百度之后找到了问题所在:
ToolProvider用到的是jdk里的tool.jar包,而jre里没有这个jar文件,而我使用的就是jre,所以报了空指针异常,将jre换成jdk后问题便得到了解决,通过这个问题我也明白了jdk是开发用的,jre是运行用的,并且jre里的东西没有jdk全,所以用jdk更好
动态运行
通过Runtime.getRuntime()运行启动新的进程运行
public class DynamicComplier { public static void main(String[] args) { JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); int result=compiler.run(null, null, null,"E:/MyJava/Myjava.java"); System.out.println(result==0?"编译成功":"编译失败"); Runtime run=Runtime.getRuntime(); Process process; try { process = run.exec("java -cp E:/MyJava Myjava"); InputStream in=process.getInputStream(); BufferedReader reader=new BufferedReader(new InputStreamReader(in)); String str=""; while((str=reader.readLine())!=null) { System.out.println(str); } } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } }
通过反射运行编译好的类
try { URL[] urls=new URL[] {new URL("file:/"+"e:/myJava/")}; URLClassLoader loader=new URLClassLoader(urls); Class c=loader.loadClass("Myjava"); //调用加载类的main方法 Method m=(Method) c.getMethod("main", String[].class).invoke(null,(Object)new String[] {"aa","bb"}); //可变参数是JDK5.0之后才有的,上述代码会编写成m.invoke(null,"aa","bb"), //这样一来就与Main函数发生了参数不匹配的问题。 }catch (Exception e) { e.printStackTrace(); }