1、通过代码如何编译java文件
编译器是一个命令行工具(jdk自带的编译工具javac,了解javac看这里:javac是什么),但也可以使用API来调用(一般的IDE都会使用这一组API来封装自己的编译功能)。编译器遵循Java语言规范(The Java Language Specification,JLS)和Java虚拟机规范(The Java Virtual Machine Specification,JVMS)。
在Java 6之后,提供了标准包来操作Java编译器,这就是javax.tools包。我们使用这个包中的API以及其他辅助包可以定制自己的编译器。通过ToolProvider类的源码我们可以看到,javax.tools这个包中的API最终都是通过tools.jar中的com.sun.tools.javac包来调用Java编译器的。
通过代码编译java大体有如下三种方式,灵活运用这几种方式可以DIY属于自己的编译器:
通过JavaCompiler.run()
最简单的用法即使用JavaCompiler类的run方法,前3个参数分别为:输入信息、输出信息、错误信息,如果为null则默认为:System.in、System.out、System.err。最后一个参数为javac后的命令文本,例如传入Test.java,则等同于在终端执行javac Test.java。
例如:
public class Test {
public static void main(String[] args) throws Exception {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int run = compiler.run(null, null, null, "-version");
System.out.println("===" + run);
}
}
执行结果(由于输出信息没有指定,默认打印在System.out中):
javac 1.7.0_79
===0
通过JavaCompiler.getTask()编译硬盘中代码
使用JavaCompiler.run方法非常简单,但它确不能更有效地得到我们所需要的信息。一般来说我们都会使用StandardJavaFileManager类(jdk 6或以上),这个类可以很好地控制输入、输出,并且可以通过DiagnosticListener得到诊断信息,而DiagnosticCollector类就是listener(监听)的实现。
具体实例如下:
public class Test {
public static void main(String[] args) throws Exception {
Test.compiler();
}
public static void compiler() throws IOException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 建立DiagnosticCollector对象
DiagnosticCollector diagnostics = new DiagnosticCollector();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
// 建立源文件对象,每个文件被保存在一个从JavaFileObject继承的类中
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromStrings(Arrays
.asList("/Users/sunjie/Desktop/works/workspace/my-test/src/com/test/Test.java"));
// options命令行选项
Iterable<String> options = Arrays.asList("-d",
"/Users/sunjie/Desktop/works/workspace/my-test/src/com/test/classes");// 指定的路径一定要存在,javac不会自己创建文件夹
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null,
compilationUnits);
// 编译源程序
boolean success = task.call();
fileManager.close();
System.out.println((success) ? "编译成功" : "编译失败");
// 打印信息
for (Object object : diagnostics.getDiagnostics()) {
Diagnostic diagnostic = (Diagnostic) object;
System.out.printf("Code: %s%n" + "Kind: %s%n" + "Position: %s%n" + "Start Position: %s%n"
+ "End Position: %s%n" + "Source: %s%n" + "Message: %s%n", diagnostic.getCode(),
diagnostic.getKind(), diagnostic.getPosition(), diagnostic.getStartPosition(),
diagnostic.getEndPosition(), diagnostic.getSource(), diagnostic.getMessage(null));
}
}
}
运行结果如下:
编译成功
在对应路径下会发现com/test/Test.class文件(Test的包是package com.test,所以会自动在对应目录下生成com/test/路径)。
通过JavaCompiler .getTask()编译内存中代码
JavaCompiler不仅可以编译硬盘上的Java文件,而且还可以编译内存中的Java代码,然后使用reflection来运行它们。我们可以编写一个JavaSourceFromString类,通过这个类可以输入Java源代码。
具体实例如下:
public class Test {
public static void main(String[] args) throws Exception {
Test.compiler2();
}
?public static void compiler2() throws IOException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector diagnostics = new DiagnosticCollector();
// 定义一个StringWriter类,用于写Java程序
StringWriter writer = new StringWriter();
PrintWriter out = new PrintWriter(writer);
// 开始写Java程序
out.println("public class HelloWorld {");
out.println(" public static void main(String args[]) {");
out.println(" System.out.println(\"Hello, World\");");
out.println(" }");
out.println("}");
out.close();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
// 为这段代码取个名子:HelloWorld
SimpleJavaFileObject file = (new Test()).new JavaSourceFromString("HelloWorld", writer.toString());
Iterable compilationUnits = Arrays.asList(file);
// options命令行选项
Iterable<String> options = Arrays.asList("-d",
"/Users/sunjie/Desktop/works/workspace/my-test/src/com/test/classes");// 指定的路径一定要存在,javac不会自己创建文件夹
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null,
compilationUnits);
boolean success = task.call();
System.out.println((success) ? "编译成功" : "编译失败");
}
// 用于传递源程序的JavaSourceFromString类
class JavaSourceFromString extends SimpleJavaFileObject {
final String code;
JavaSourceFromString(String name, String code) {
super(URI.create("string:///" + name.replace(‘.‘, ‘/‘) + Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
}
运行结果如下:
编译成功
在对应路径下会发现HelloWorld.class文件。