在程序中实现对java源文件编译的3种方法

一般情况下对java源文件的编译均是在代码完成后使用javac编译的,不管是使用IDE还是直接使用命令行。这里要说的情况是比较特别的,就是在代码内动态的编译一些代码。比如你想通过在某个目录下通过放置一些源代码的方式来实现对程序功能的动态扩展,那么你的程序就需要具有一种对源代码的编译、加载、运行的能力,可能就需要本文介绍的3种方法。

[align=center][color=blue][b]方法1:通过调用本机的javac命令来编译。[/b][/color][/align]
在java程序中调用javac命令可以通过调用Runtime类的exec或是ProcessBuilder类的start方法来完成,这两个类的功能基本相同,用法也比较相似,这里的例子我们就用ProcessBuilder来演示。如果是JDK1.5之前的版本请使用Runtime类完成相同的功能。

开始之前先来点准备工作,将下面的类放到 c:\mytest\src\ 目录下,这个类我们不会在IDE中编译,而是由我们程序完成其编译。保存时使用UTF-8格式。可以直接在附件中下载这个类。

public class HelloWorld {
public void sayHello(String in)
{
System.out.println("动态编译成功");
System.out.println("使用编译方式:" + in);
}

}


准备工作完成,下面就看一下我们程序的代码,这里只列出主要代码

public class JavacCompile {

private static String filePath = "c:\\mytest\\src\\HelloWorld.java";
private static String binDir = "c:\\mytest\\bin";

public static void main(String[] args) {
File binOutDir = new File(binDir);
if (!binOutDir.exists())
{
binOutDir.mkdirs();
}

// 设置javac的编译参数,使用-encoding指定编码方式,-d并指定编译生成class文件目录
ProcessBuilder pb = new ProcessBuilder("javac","-encoding", "UTF-8","-d", binDir, filePath);
try {
// 开始调用javac命令编译
final Process proc = pb.start();

// 处理进程的输出,避免挂死
new Thread(new Runnable() {
public void run() {
processStream(proc.getInputStream());
processStream(proc.getErrorStream());
}
}).start();

// 等待编译完成
proc.waitFor();

// 加载编译好的类,并调用相应的方法
new LoaderClassByDir(binDir).execSayHello("javac");

} catch (Exception ex) {
Logger.getLogger(JavacCompile.class.getName()).log(Level.SEVERE, null, ex);
}
}

private static void processStream(InputStream stderr) {
...
}
}



LoaderClassByDir类的代码会保含在后面的上传的文件中,因为这里主要介绍完成程序中对java源文件的编译,对于类的加载和运行不多做描述,可以参考LoaderClassByDir类中的简单实现。
[align=center]
[color=blue][b]方法2:使用Sun的tools.jar包时的com.sun.tools.javac.Main类完成对代码的编译。[/b][/color][/align]
注意这个类的是在tools.jar包里,tools.jar不是标准的Java库,在使用时必须要设置这个jar的路径,使用IDE时需要显示的引入到编译路径中,不然会找不到。我们使用此类改写上面的编译类如下:

public class JavacCompile {

private static String filePath = "c:\\mytest\\src\\HelloWorld.java";
private static String binDir = "c:\\mytest\\bin";

public static void main(String[] args) {
File binOutDir = new File(binDir);
if (!binOutDir.exists())
{
binOutDir.mkdirs();
}

// 将编译参数通过数组传递到编译方法中,该函数的方法和javac的参数完成一致
Main.compile(new String[]{"-encoding", "UTF-8","-d", binDir, filePath});

try {
// 加载编译好的类,并调用相应的方法
new LoaderClassByDir(binDir).execSayHello("sun tools");
} catch (Exception ex) {
Logger.getLogger(JavacCompile.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

使用这个类后,同样的功能代码变得更加简洁。


[align=center][color=blue][b]方法3:使用javax.tools包[/b][/color][/align]
从上面可以看到方法2的缺点就是tools.jar需要我们自行导入。而在Java SE6中为我们提供了标准的包来操作Java编译器,这就是javax.tools包。使用这个包,我们可以不用将jar文件路径添加到classpath中了。 使用这个类的方法和上面的类很相似,我只需要将

Main.compile(new String[]{"-encoding", "UTF-8","-d", binDir, filePath});

替换成:

// 将编译参数通过数组传递到编译方法中,该函数的方法和javac的参数完成一致
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, "-encoding", "UTF-8","-d", binDir, filePath);

就可以完成相应的编译功能,这里简介一下run的使用方法:
[quote]我们可以通过ToolProvider类的静态方法getSystemJavaCompiler来得到一个JavaCompiler接口的实例。

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

JavaCompiler中最核心的方法是run。通过这个方法可以编译java源程序。这个方法有3个固定参数和1个可变参数(可变参数是从 Jave SE5开始提供的一个新的参数类型,用type… argu表示)。前3个参数分别用来为java编译器提供参数、得到Java编译器的输出信息以及接收编译器的错误信息,后面的可变参数可以传入一个或多个Java源程序文件。如果run编译成功,返回0。

int run(InputStream in, OutputStream out, OutputStream err, String... arguments)

如果前3个参数传入的是null,那么run方法将以标准的输入、输出代替,即System.in、System.out和System.err。[/quote]

注意:使用上传文件中的代码做测试时,为避免上次编译的影响记得手动删除C:\mytest\bin下的类文件
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值