最近实现了一个需求,测试JAVA代码的可行性,所以刚开始打算用java 调用cmd执行进行编译,不料 jdk环境1.8下,用java无法调用其他文件下的类,(也许可以,但是我折腾了2个多小时放弃了),所以就查询大量的资料,手撸了一个编译类,代码多亏有前人的帮助,才能帮我实现这个接口,不过忘了是哪个博客了(说声抱歉)。希望这个代码能帮助大家实现Java代码测评。
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
public class Compile {
/*
* 调用前设置 setCode() 数据
* setabc() 数据
* 运行时间 getUseTime()
* 内存使用 getUseMemory()
* 运行结果 getOutMsg()
* 编译错误 getCE()
* 运行错误 getError()
*
* setabc() 没有完善。不过不影响测试数据使用。
*
*/
public static void main(String[] args) {
Compile cr = new Compile();
cr.setCode("public class Main{\r\n" +
" public static void main(String[] args){\r\n" +
" Scanner sc = new Scanner(System.in);\r\n" +
" int a = sc.nextInt();\r\n" +
" int b = sc.nextInt();\r\n" +
" System.out.println(a);\r\n" +
" System.out.println(b);\r\n" +
" }\r\n" +
"}");
cr.setabc("1415445\r\n1");
cr.compileAndRunJavaFile(cr.getCode());
if(cr.isCompileAndRunOK()) {
System.out.println("运行时间: " + cr.getUseTime() + "ms");
System.out.println("内存使用: " + cr.getUseMemory() + "kb9");
System.out.println("运行结果: \n" + cr.getOutMsg());
} else if(cr.isCompilerError()) {
System.out.println("编译错误: " + cr.getCE());
} else if(cr.isRunningError()) {
System.out.println("运行错误: " + cr.getError());
}
String a = cr.getOutMsg();
String b = cr.getOutMsg();
System.out.println(a.equals(b));
System.out.println(b.equals("1415445\n" +
"1\n"));
}
private String abc = "";
public void setabc(String abc) {
this.abc=abc;
}
public String getabc() {
return this.abc;
}
private String code = "";
public void setCode(String code) {
this.code=code;
}
public String getCode() {
return this.code;
}
//编译错误
private StringBuilder ce = new StringBuilder();
public String getCE(){
return ce.toString();
}
//内存使用
private double useMemory = 0.0;
public double getUseMemory(){
return useMemory;
}
//运行时间
private long useTime = 0;
public long getUseTime(){
return useTime;
}
//输出信息
private StringBuilder outMsg = new StringBuilder();
public String getOutMsg(){
return outMsg.toString();
}
//异常信息
private String error = null;
public String getError(){
return error;
}
//是否正常编译并运行
private boolean isCompileAndRunOK = false;
public boolean isCompileAndRunOK(){
return isCompileAndRunOK;
}
//程序的运行时间, 单位:ms
private int limitTime = 2000;
//程序所占内存, 单位 :KB
private double limitMemory = 256000.0;
public void setLimitTime(int limitTime){
this.limitTime = limitTime;
}
public void setLimitMemory(double limitMemory){
this.limitMemory = limitMemory;
}
//是否为编译错误
private boolean isCompilerError = false;
public boolean isCompilerError(){
return isCompilerError;
}
//是否为运行错误
private boolean isRunningError = false;
public boolean isRunningError(){
return isRunningError;
}
private static final String className = "Main";
private static final String methodName = "main";
private String getClassOutput(){
//设置class文件的存放位置
return "D:\\";
}
private void compileAndRunJavaFile(String code){
PrintStream ps = null;
BufferedReader br = null;
//保存标准输出流
InputStream stdIn = System.in;
//保存标准输入流
PrintStream stdOut = System.out;
ByteArrayInputStream stream =null;
//为源代码导入默认的包
code = "import java.util.*;\n" + code;
try {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> oDiagnosticCollector = new DiagnosticCollector<JavaFileObject>();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(oDiagnosticCollector, null, null);
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(new File[] { new File(getClassOutput()) }));
StringSourceJavaObject sourceObject = new Compile.StringSourceJavaObject(className, code);
Iterable<? extends JavaFileObject> fileObjects = Arrays.asList(sourceObject);
CompilationTask task = compiler.getTask(null, fileManager, oDiagnosticCollector, null, null, fileObjects);
boolean result = task.call();
if (result) {
Runtime runtime = Runtime.getRuntime();
MyClassLoader mcl = new MyClassLoader();
Class<?> clazz = Class.forName("Main", true, mcl);
Method method = clazz.getMethod(methodName, new Class<?>[]{String[].class});
//重置输入流,需要get到测试数据
String abc = getabc();
stream = new ByteArrayInputStream(abc.getBytes());
System.setIn(stream);
//重置输出流,需要获得控制台的输出
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ps = new PrintStream(bao);
System.setOut(ps);
long startFreeMemory = runtime.freeMemory();//Java 虚拟机中的空闲内存量
//执行时间也是无法知道,因为dos执行java命令,程序无法知道它到底执行到那里了,两个进程,互不了解
long startCurrentTime = System.currentTimeMillis();//获取系统当前时间
method.invoke(null, new Object[]{null});
long endCurrentTime = System.currentTimeMillis();
long endFreeMemory = runtime.freeMemory();
//内存的使用情况,不是很精确
useMemory = (startFreeMemory-endFreeMemory)/1024.0;
if(useMemory > limitMemory) throw new Exception("Out Limit Memory!");
useTime = endCurrentTime-startCurrentTime;
if(useTime > limitTime) throw new Exception("Time Limited!");
//获得控制台的输出
br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bao.toByteArray())));
String outc = null;
while((outc = br.readLine()) != null)
outMsg.append(outc).append("\n");
//正常编译并运行
isCompileAndRunOK = true;
} else {
isCompilerError = true;
//打印编译的错误信息
Pattern p = Pattern.compile("Main.java\\D*(\\d+):", Pattern.DOTALL);
for (Diagnostic<? extends JavaFileObject> oDiagnostic : oDiagnosticCollector.getDiagnostics()){
//将行号减1
Matcher m = p.matcher("Compiler Error: " + oDiagnostic.getMessage(null));
if(m.find()) {
ce.append(m.replaceAll("Main.java " + String.valueOf(Integer.valueOf(m.group(1))-1)) + ":").append("\n");
} else {
ce.append("Compiler Error: " + oDiagnostic.getMessage(null)).append("\n");
}
}
}
} catch (Exception e) {
isRunningError = true;
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw, true));
Pattern p = Pattern.compile("Main.java\\D*(\\d+)", Pattern.DOTALL);
Matcher m = p.matcher(sw.toString());
if(m.find()){
error = m.replaceAll("Main.java " + String.valueOf(Integer.valueOf(m.group(1))-1) + ":");
} else {
error = sw.toString();
}
} finally {
//关闭流
try {
if(stream != null)
stream.close();
if(ps != null)
ps.close();
if(br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
//恢复输入输出流
System.setIn(stdIn);
System.setOut(stdOut);
}
}
private class StringSourceJavaObject extends SimpleJavaFileObject {
private String content = null;
public StringSourceJavaObject(String name, String content) {
super(URI.create(name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.content = content;
}
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return content;
}
}
}
class MyClassLoader extends ClassLoader
{
public MyClassLoader()
{
}
public MyClassLoader(ClassLoader parent)
{
super(parent);
}
protected Class<?> findClass(String name) throws ClassNotFoundException
{
File file = getClassFile(name);
try
{
byte[] bytes = getClassBytes(file);
Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
return c;
}
catch (Exception e)
{
e.printStackTrace();
}
return super.findClass(name);
}
private File getClassFile(String name)
{
File file = new File("D://Main.class");
return file;
}
private byte[] getClassBytes(File file) throws Exception
{
// 这里要读入.class的字节,因此要使用字节流
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
WritableByteChannel wbc = Channels.newChannel(baos);
ByteBuffer by = ByteBuffer.allocate(1024);
while (true)
{
int i = fc.read(by);
if (i == 0 || i == -1)
break;
by.flip();
wbc.write(by);
by.clear();
}
fis.close();
return baos.toByteArray();
}
}