• 对.java文件即时编译
  • 对字符串即时编译
  • 监听在编译过程中产生的警告和错误
  • 在代码中运行编译器(并非:Runtime命令行调用javac命令)

JavaXompiler类

  1. 自Java1.6推出,位于javax.tools包中
  2. 可用在程序文件中的Java编译器接口(代替javac.exe)
  3. 在程序中编译Java文件,产生class文件
  4. run方法(继承自java.tools.Tools):比较简单。可以编译Java源文件,生成class文件,但不能指定输出路径,监控报错信息,调用后就在源码所在目录生成class文件
  5. 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编译
  • 在线编译环境
  • 在线程序评判系统
  • 自动化的构建和测试工具