一,通过JavaCompiler动态编译
public static void main(String[] args) {
String str = "public class Demo01 {public static void main(String[] args){System.out.println(\"HaHa,heihei!\");}}";
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int result = compiler.run(null, null, null, "D:/java/Demo01.java");
System.out.println(result);
}
步骤:
①写要编译的内容
②通过ToolProvider的一个方法getSystemJavaCompiler()获取JavaCompiler对象
③调用JavaCompiler.run();
这个函数有四个参数:
第一个参数:为java编译器提供参数
第二个参数:得到java编译器的输出信息
第三个参数:接收编译器的错误信息
第四个参数:可变参数(是一个String数组)能传入一个或多个Java源文件
返回值:0扁食编译成功,非0表示编译失败
PS:在执行的时候可能会出现异常如下:
Exception in thread "main" java.lang.NullPointerException
at com.vvvvvv.TestCompiler.Demo01.main(Demo01.java:11)
这是由于第②步执行的时候,返回的是Null。因此在调用compiler.run的时候抛出了空指针异常。
为啥会这样呢?查看了下ToolProvider的源代码,发现如下这么一段….
private Class<?> findSystemToolClass(String toolClassName)
throws MalformedURLException, ClassNotFoundException
{
// try loading class directly, in case tool is on the bootclasspath
try {
return Class.forName(toolClassName, false, null);
} catch (ClassNotFoundException e) {
trace(FINE, e);
// if tool not on bootclasspath, look in default tools location (tools.jar)
ClassLoader cl = (refToolClassLoader == null ? null : refToolClassLoader.get());
if (cl == null) {
File file = new File(System.getProperty("java.home"));
if (file.getName().equalsIgnoreCase("jre"))
file = file.getParentFile();
for (String name : defaultToolsLocation)
file = new File(file, name);
// if tools not found, no point in trying a URLClassLoader
// so rethrow the original exception.
if (!file.exists())
throw e;
URL[] urls = { file.toURI().toURL() };
trace(FINE, urls[0].toString());
cl = URLClassLoader.newInstance(urls);
refToolClassLoader = new WeakReference<ClassLoader>(cl);
}
return Class.forName(toolClassName, false, cl);
}
}
我已经设置了JAVA_HOME环境变量,指向了我的JRE安装目录C:\Java\jre6, 注意查找文件的代码…
for (String name : defaultToolsLocation)
file = new File(file, name);
也就是说会查找目录C:\Java\jre6\lib\tools.jar
但是注意的是tools.jar并不在jre中,而是在jdk安装目录下,因此我手动把这个jar文件从jdk目录下拷贝到了jre目录下,再次运行上面的代码就没有问题了!同时注意到在代码工程目录下生成了一个名为Main.class的文件,这个确实说明了动态类Main已经编译成功了!
利用IO流将str读到文件中进行编译
较不规范。
public static void main(String[] args) throws Exception {
String str = "public class Demo01 {public static void main(String[] args){System.out.println(\"HaHa,heihei!\");}}";
String dest = "D:/java/Demo01.java";
File f = new File(dest);
OutputStream os = null;
os = new FileOutputStream(f,true);
byte[] bytes = str.getBytes();
os.write(bytes, 0, bytes.length);
os.flush();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int result = compiler.run(null, null, null,dest);
System.out.println(result==0?"编译成功":"编译失败");
os.close();
}
二,通过Runtime.getRuntime()运行编译好的类
步骤:
①通过run.exec(String str)运行,返回一个Process对象
②通过process.getInputStream()返回一个输入流
③将字节流转化为字符流
④读取出运行结果
//进行动态编译
Runtime run = Runtime.getRuntime();
Process process= run.exec("java -cp d:/java Demo01");
InputStream in = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String info = "";
while((info = reader.readLine()) != null){
System.out.println(info);
}
三,通过反射运行编译好的类
步骤:
①将路径存入URL[]中。
②用类加载器加载我们要运行的类
③获取加载的类
④获取要调用的方法
⑤执行方法
URL[] urls = new URL[] {new URL("file:/"+"d:/java/")};
URLClassLoader classLoader = new URLClassLoader(urls);
Class c = classLoader.loadClass("Demo01");
Method m = c.getMethod("main", String[].class);
m.invoke(null, (Object)new String[]{});
注意:
m.invoke(null, (Object)new String[]{});中必须加上Object转型。
由于可变参数是JDK5.0之后才有。
m.invoke(null, (Object)new String[]{});会编译成:m.invoke(null,”aa”,”bb”),就发生了参数个数不匹配的问题。
因此,必须要加上(Object)转型,避免这个问题。