- 对.java文件即时编译
- 对字符串即时编译
- 监听在编译过程中产生的警告和错误
- 在代码中运行编译器(并非:Runtime命令行调用javac命令)
JavaXompiler类
- 自Java1.6推出,位于javax.tools包中
- 可用在程序文件中的Java编译器接口(代替javac.exe)
- 在程序中编译Java文件,产生class文件
- run方法(继承自java.tools.Tools):比较简单。可以编译Java源文件,生成class文件,但不能指定输出路径,监控报错信息,调用后就在源码所在目录生成class文件
- getTask方法:更强大的功能。可以编译Java源文件,包括在内存中的Java文件(字符串),生成class文件
// 编译成功函数
public static void successCompile() {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 参数1:输入流,null表示默认使用system.in
// 参数2:输出流,null表示默认使用system.out
// 参数3:错误流,null表示默认使用system.err
// 参数4:String... 需要编译的路径
// 返回值:0表示成功,其他表示错误
int result = compiler.run(null, null, null,
"E:\\TangJiachang\\java学习\\JavaDome1\\src\\exmple\\com\\demo4\\A.java");
System.out.println(0 == result ? "编译成功" : "编译失败");
}
注意:编译后的class文件和java文件在同一个目录
public static void failCompile() throws UnsupportedEncodingException {
ByteArrayOutputStream err = new ByteArrayOutputStream();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int result = compiler.run(null, null, null,
"E:\\TangJiachang\\java学习\\JavaDome1\\src\\exmple\\com\\demo4\\B.java");
if (0 == result) {
System.out.println("成功");
} else {
System.out.println("失败");
System.out.println(new String(err.toByteArray(), Charset.defaultCharset().toString()));
}
}
由于指定的目录中没有B.java文件所以报错
对字符串进行编译
public class Demo2 {
public static void main(String[] args) {
compileJavaFromString();
}
public static void compileJavaFromString() {
StringBuilder sb = new StringBuilder();
// 类名
String className = "Hello";
sb.append("public class Hello {\n");
sb.append("public static void main(String[] args) {\n");
sb.append("System.out.println(\"hello world\");\n");
sb.append("}\n");
sb.append("}");
// 将上述的代码编译
Class<?> c = compile(className, sb.toString());
try {
// 生成对象
Object obj = c.newInstance();
// 调用main方法,必须传递String[].class参数
Method m = c.getMethod("main", String[].class);
// 运行main方法
m.invoke(obj, new Object[] {new String[] {} });
} catch (Exception e) {
e.printStackTrace();
}
}
private static Class<?> compile(String className, String javaCodes) {
// 将字符串包装成SimpleJavaFileObject对象
JavaSourceFromString srcObject = new JavaSourceFromString(className, javaCodes);
// 输出代码内容
System.out.println(srcObject.getCode());
Iterable<? extends JavaFileObject> fileObjects = Arrays.asList(srcObject);
// javac.exe接口
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 文件管理器
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
// 报告诊断信息对象
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
//设置编译的输出目录,并包装在options中
String flag = "-d";
// 编译输出路径
String outDir = "";
try {
File classPath = new File(Thread.currentThread().getContextClassLoader().getResource("").toURI());
outDir = classPath.getAbsolutePath() + File.separator;
// 输出编译后的文件路径
System.out.println(outDir);
} catch (URISyntaxException e1) {
e1.printStackTrace();
}
// 等价于javac -d outPath javaFile
Iterable<String> options = Arrays.asList(flag, outDir);
// JavaCompiler.getTask方法:以future的任务形式(多线程),来执行编译任务
// 第一个参数:额外输出流,null表示默认使用system.err
// 第二个参数:文件管理器,null表示编译器标准文件管理器
// 第三个参数:诊断监听器,null表示使用编译器默认方法来报告诊断信息
// 第四个参数:编译器参数,null表示无参数
// 第五个参数:需要经过annotation处理的类名,null表示没有类需要annotation处理
// 第六个参数:待编译的类
JavaCompiler.CompilationTask task =
compiler.getTask(null, fileManager, diagnosticCollector, options, null, fileObjects);
// 等待编译结束
boolean result = task.call();
if (result == true) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} else {
for (Diagnostic diagnostic : diagnosticCollector
.getDiagnostics()) {
System.out.println("Error on line: "
+ diagnostic.getLineNumber() + "; URI: "
+ diagnostic.getSource().toString());
}
}
return null;
}
}
// 由于我们的代码是一串字符串没有存储到实际的文件中
// 所以使用本类来虚拟一个文件来存储对应的code
class JavaSourceFromString extends SimpleJavaFileObject {
private String code;
protected JavaSourceFromString(String name, String code) {
super(URI.create("string:///" + name.replace(".", "/")
+ Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
public String getCode() {
return code;
}
}
由于对字符串的编译时,字符串是在内存中的,所以需要使用SimpleJavaFileObject这个类来虚拟一个文件用来存储对应的字符串,这样子就可以使用反射机制来读取该文件。
Java编译器API作用
- Java EE的JSP编译
- 在线编译环境
- 在线程序评判系统
- 自动化的构建和测试工具