一般情况下对java源文件的编译均是在代码完成后使用javac编译的,不管是使用IDE还是直接使用命令行。这里要说的情况是比较特别的,就是在代码内动态的编译一些代码。比如你想通过在某个目录下通过放置一些源代码的方式来实现对程序功能的动态扩展,那么你的程序就需要具有一种对源代码的编译、加载、运行的能力,可能就需要本文介绍的3种方法。
方法1:通过调用本机的javac命令来编译。
在java程序中调用javac命令可以通过调用Runtime类的exec或是ProcessBuilder类的start方法来完成,这两个类的功能基本相同,用法也比较相似,这里的例子我们就用ProcessBuilder来演示。如果是JDK1.5之前的版本请使用Runtime类完成相同的功能。
开始之前先来点准备工作,将下面的类放到 c:\mytest\src\ 目录下,这个类我们不会在IDE中编译,而是由我们程序完成其编译。保存时使用UTF-8格式。可以直接在附件中下载这个类。
Java代码
public class HelloWorld {
public void sayHello(String in)
{
System.out.println("动态编译成功");
System.out.println("使用编译方式:" + in);
}
}
准备工作完成,下面就看一下我们程序的代码,这里只列出主要代码
Java代码
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类中的简单实现。
方法2:使用Sun的tools.jar包时的com.sun.tools.javac.Main类完成对代码的编译。
注意这个类的是在tools.jar包里,tools.jar不是标准的Java库,在使用时必须要设置这个jar的路径,使用IDE时需要显示的引入到编译路径中,不然会找不到。我们使用此类改写上面的编译类如下:
Java代码
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);
}
}
}
使用这个类后,同样的功能代码变得更加简洁。
方法3:使用javax.tools包
从上面可以看到方法2的缺点就是tools.jar需要我们自行导入。而在Java SE6中为我们提供了标准的包来操作Java编译器,这就是javax.tools包。使用这个包,我们可以不用将jar文件路径添加到classpath中了。 使用这个类的方法和上面的类很相似,我只需要将
Java代码
Main.compile(new String[]{"-encoding", "UTF-8","-d", binDir, filePath});
替换成:
Java代码
// 将编译参数通过数组传递到编译方法中,该函数的方法和javac的参数完成一致
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, "-encoding", "UTF-8","-d", binDir, filePath);
就可以完成相应的编译功能,这里简介一下run的使用方法:
引用
我们可以通过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。
注意:使用上传文件中的代码做测试时,为避免上次编译的影响记得手动删除C:\mytest\bin下的类文件
wubo19842008 写道
用javax.tool包编译时,如果源文件依赖外部jar包,如何设置classpath啊?
这里的参数设置和javac的参数基本上是一致的,如果想指定classpath,可以指定-cp参数,使用样例如下:
Java代码
int result = compiler.run(null, null, null, "-encoding", "UTF-8", "-cp", "D:/Program Files/Java/jdk1.6.0_14/lib/tools.jar", "-d", binDir, filePath);
如上就可以将sun的tools.jar引入到编译路径中
分享到:
相关推荐
### 在程序中实现对Java源文件编译的三种方法 #### 方法一:通过调用本机的`javac`命令来编译 本方法适用于在程序内部动态编译Java源文件的情形,例如需要通过放置一些源代码的方式实现对程序功能的动态扩展。此...
### 在程序中实现对Java源文件编译的三种方法 #### 方法一:通过调用本机的`javac`命令来编译 本方法适用于在Java程序内部动态编译源代码的情形,例如需要通过放置一些源代码来实现程序功能的动态扩展。此方法通过...
Java动态编译指的是在程序运行时将Java源代码编译为字节码并加载到Java虚拟机(JVM)中的过程。这种技术在许多场景下非常有用,例如在开发环境中进行快速迭代、AOP(面向切面编程)或运行时代码生成等。Java的`javax...
这篇博客“Java类动态加载(一)——java源文件动态编译为class文件”可能主要探讨了如何在运行时将Java源代码(.java)编译成对应的字节码文件(.class),并将其加载到Java虚拟机(JVM)中。以下是对这个主题的详细解析...
Java反编译是一种技术,它允许开发者从已编译的.class文件中恢复原始的.java源代码。这在一些情况下非常有用,例如分析第三方库的内部实现、研究代码逻辑或者在丢失源代码时恢复源码。Java编译器将源代码转换成字节...
Java反编译是Java开发中一个重要的辅助工具,它能够帮助开发者查看已编译的`.class`文件中的源代码,即使原始的`.java`源文件已经丢失或未被提供。这个过程对于理解类库的工作原理、逆向工程、调试、学习或者分析...
Java编译器将Java源文件编译成Java字节码文件,扩展名为.class。编译器可以使用javac命令来编译Java源文件。在命令窗口中,使用javac命令编译指定目录下的Java源文件。 例如,在上面的示例程序中,使用javac命令...
通过java的ToolProvider创建JavaCompile,用来执行class源文件 * 4.创建DiagnosticCollector用来执行获取执行失败的错误结果 * 5.添加动态执行的编译环境 options 是个集合,添加内容,字符集,classpath等 * 6....
在我们的例子中,我们可以使用`JavaCompiler`来编译指定的Java源文件。以下是一个简单的示例: ```java import javax.tools.*; import java.io.*; import java.util.*; public class BuildRmi { public static ...
泛型是Java中用于增强类型安全性的特性,允许在编译时检查类型。通过实例,你可以学习如何定义和使用泛型类、接口和方法。 10. **反射**: 反射机制允许程序在运行时检查类的信息,如类名、方法名和属性。这在...
描述中强调了这个软件的功能,即把编译后的class文件转换回Java源文件。这在某些场景下非常有价值,例如,当你遇到一个没有源码但只有字节码的库,或者想查看某个二进制组件的内部实现时。 在Java反编译领域,有...
在Java中,多态分为编译时多态(方法重载)和运行时多态(方法重写)。运行时多态是通过接口和继承实现的。 5. **抽象**:抽象是将共同特征提取出来形成抽象类或接口的过程。抽象类不能实例化,但可以作为其他类的...
Java Class源文件查看工具是Java开发者在处理已编译的.class文件时不可或缺的辅助工具,主要用于将二进制的.class文件转换回可读性更强的.java源代码文件。这种过程通常被称为反编译。反编译对于理解第三方库的内部...
虽然手动编译和运行Java程序是了解其工作原理的重要途径,但在实际开发中,我们通常使用集成开发环境(IDE),如Eclipse、IntelliJ IDEA等,它们提供了自动编译、调试和运行的功能,极大地提高了开发效率。...
Java动态编译是一种技术,允许程序在运行时将源代码转换为字节码并加载到JVM(Java虚拟机)中。这种能力使得Java应用程序能够根据需要编译和执行新的代码,增强了软件的灵活性和可扩展性。在给定的场景中,用户通过...
接口在Java中用于定义一种合同,规定了实现该接口的类必须提供哪些方法。在源文件中,我们可能会看到如`ReservationInterface.java`这样的接口定义文件,它定义了预定服务需要实现的方法。 5. **异常(Exception)...
Java源文件打包成EXE是将Java程序转化为可以在Windows操作系统上直接执行的可执行文件的过程。这主要涉及到两个关键步骤:首先,将Java源代码编译成字节码(.class文件),然后将这些字节码打包成一个可运行的JAR...
在给定的代码片段中,我们可以看到一个名为`BatchCompiler`的Java类,该类旨在实现Java源文件的批量编译。以下是对代码中关键部分的分析: 1. **数据结构选择**:代码使用了`LinkedList<String>`来存储待处理的目录...