手撸Java编译类,并且加载实现测试输入输出。

最近实现了一个需求,测试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();
    }
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值