JAVA反射机制+动态运行编译期不存在的JAVA程序

一、有关JAVA反射

在运行期间,在不知道某个类A的内部方法和属性时,能够动态的获取信息、获取类或对象的方法、属性的功能,称之为反射。

1.相关类:

(1)Class

(2)Method

(3)Field

2.相关方法:

(1)Class.forName("<完整包名+类名>");  返回对应的类(同一JVM不管创建该类的多少对象,类只有一个)。

(2)class.getMethod(要获取的方法名,参数类型,参数类型.....);  返回该方法。

(3)执行方法method.invoke(实例,参数,参数.....)  返回该方法的返回值(Object类型,可强转为指定类型)。

(4)class.newInstance()  返回一个实例对象,通常与(3)一起使用。

(5)class.getFields()  返回该对象的所有共有属性。

(6)class.getDeclaredFields  获取对象的共有+私有属性。

二、应用

1.简介:

公司有个项目,需要将提交上来的Java源码进行编译与运行,并返回结果。一般的思想是JVM-命令行-JVM的模式。但是这样不但执行速度慢,且获取编译异常、运行异常、运行结果都是非常困难的。所以通过JAVA反射机制,以及自带的编译工具完成功能。

2.代码:

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider; public class RunStudentPro {
private static String CLASSPATH;
/**
* 程序运行成功
*/
private static final boolean SUCCESS = true;
/**
* 程序运行失败
*/
private static final boolean DEFAULT = false; /**
* 编译期的错误列表
*/
private List<String> bianYiError; /**
* 学生运行的结果
*/
private Object studenResult; /**
* 运行时异常
*/
private List<String> runTimeErrors; /**
* 运行学生程序的主方法
*
* @param pro
* 学生代码
* @param javaName
* 学生类名
* @param methodName
* 学生方法
* @return
*/
public boolean runStudentProgram(String pro, String javaName, String methodName, String studentID) {
// 获取类所在的路径
CLASSPATH = this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath(); if (System.getProperty("os.name").contains("dows")) {
CLASSPATH = CLASSPATH.substring(1, CLASSPATH.length());
} if (CLASSPATH.endsWith(".jar")) {
CLASSPATH = CLASSPATH.substring(0, CLASSPATH.lastIndexOf("/") + 1);
} CLASSPATH = CLASSPATH + studentID + "/"; // 在本地写入java文件
writejavaFile(CLASSPATH, javaName, pro);
// 编译java文件
boolean javaCompilerFile = JavaCompilerFile(javaName); if (!javaCompilerFile) {
// 编译期失败
return DEFAULT;
} boolean runStudentProgram = runStudentProgram(javaName, methodName); if (!runStudentProgram) {
// 运行失败
return DEFAULT;
} return SUCCESS;
} /**
* 编译学生的java文件
*
* @param javaName
* @return
*/
private boolean JavaCompilerFile(String javaName) {
StandardJavaFileManager javafile = null;
boolean result = SUCCESS; try {
JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
javafile = javac.getStandardFileManager(null, null, null);
DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
String filename = CLASSPATH + javaName + ".java";
Iterable<? extends JavaFileObject> units = javafile.getJavaFileObjects(filename);
CompilationTask t = javac.getTask(null, javafile, collector, null, null, units); if (!t.call()) {
List<Diagnostic<? extends JavaFileObject>> diagnostics = collector.getDiagnostics();
bianYiError = new ArrayList<String>();
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics) {
bianYiError.add(diagnostic.toString());
}
result = DEFAULT;
}
} finally {
try {
if (javafile != null) {
javafile.close();
}
} catch (IOException e) {
e.printStackTrace();
}
} return result;
} /**
* 运行学生的程序
*
* @param javaName
* 主类名
* @param methodName
* 方法名
* @return
*/
private boolean runStudentProgram(String javaName, String methodName) {
URLClassLoader classload = null;
boolean result = SUCCESS; try {
URL url = new URL("file:/" + CLASSPATH);
URL[] urls = new URL[] { url };
classload = new URLClassLoader(urls);
Class<?> clazz = classload.loadClass(javaName);
Method method = clazz.getMethod(methodName);
studenResult = method.invoke(clazz.newInstance());
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException | InstantiationException
| MalformedURLException e) {
runTimeErrors = new ArrayList<String>();
runTimeErrors.add(getExceptionAllinformation(e));
result = DEFAULT;
} finally {
if (classload != null) {
try {
classload.close();
} catch (IOException e) {
e.printStackTrace();
result = DEFAULT;
}
}
} return result;
} /**
* 获取异常的所有信息并转换为字符串
*
* @param ex
* @return
*/
private static String getExceptionAllinformation(Exception ex) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream pout = new PrintStream(out);
ex.printStackTrace(pout);
String ret = new String(out.toByteArray());
pout.close();
try {
out.close();
} catch (Exception e) {
}
return ret;
} /**
* 创建java文件
*
* @param path
* 路径
* @param javaName
* 文件名
* @param pro
* 程序字符串
*/
private static void writejavaFile(String path, String javaName, String pro) {
File dir = new File(path); if (!dir.exists()) {
dir.mkdir();
} File file = new File(path + javaName + ".java"); if (file.exists()) {
file.delete();
}
FileWriter fw = null;
try {
file.createNewFile();
fw = new FileWriter(file);
fw.write(pro);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} /**
* 获取编译时的错误信息
*
* @return
*/
public List<String> getBianYiError() {
return bianYiError;
} /**
* 获取学生运行后的结果
*
* @return
*/
public Object getStudenResult() {
return studenResult;
} /**
* 获取学生运行时的异常
*
* @return
*/
public List<String> getRunTimeErrors() {
return runTimeErrors;
}
}

3.详细讲解:

(1)程序入口为public boolean runStudentProgram(String pro, String javaName, String methodName, String studentID)

通过传入代码、主类名称、要执行的方法名、学生ID,进行保存代码,编译代码、运行代码。

其中,this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath()获取当前类所在的目录(主要是方便移植,此路径可写死或修改为指定目录),然后将.jar及开头的'/'符号去除。

(2)编译学生代码:

通过JavaCompiler类进行编译(JDK1.6提供),使用方式为JavaCompiler javac = ToolProvider.getSystemJavaCompiler();

注意的是,编译器接口是在javax.tools包里,所以可能IDE所配置的JRE不存在此类,所以报错的话直接更改JRE。

可以通过CompilationTask t = javac.getTask(null, javafile, collector, null, null, units);中设置collector获取编译的错误信息。

其中编译方法的运行为call()方法,也可以javac.run();

(3)运行学生代码:

method.invoke(clazz.newInstance());因为我们规定了不允许传入参数,所以此处只是传入了实例,并没有传入参数。

运行时的错误信息,可以通过异常Exception指定输出流进行保存。本程序的getExceptionAllinformation方法即为实现方法。

三、运行效果截图

1.正常运行,传入的代码如下

运行结果如下:

2.模拟编译时异常,传入代码如下:

运行结果如下:

3.模拟运行时异常,传入代码如下:

运行结果如下:

java反射机制以及应用的更多相关文章

  1. 第28章 java反射机制

    java反射机制 1.类加载机制 1.1.jvm和类 运行Java程序:java 带有main方法的类名 之后java会启动jvm,并加载字节码(字节码就是一个类在内存空间的状态) 当调用java命令 ...

  2. Java反射机制

    Java反射机制 一:什么事反射机制 简单地说,就是程序运行时能够通过反射的到类的所有信息,只需要获得类名,方法名,属性名. 二:为什么要用反射:     静态编译:在编译时确定类型,绑定对象,即通过 ...

  3. java基础知识(十一)java反射机制(上)

    java.lang.Class类详解 java Class类详解 一.class类 Class类是java语言定义的特定类的实现,在java中每个类都有一个相应的Class对象,以便java程序运行时 ...

  4. java基础知识(十一)java反射机制(下)

    1.什么是反射机制? java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象都能够调用他的属性和方法,这种动态获取属性和方法的功能称为java的反射机制. ...

  5. Java反射机制专题

    ·Java Reflection Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方 ...

  6. java反射机制深入详解

    java反射机制深入详解  转自:http://www.cnblogs.com/hxsyl/archive/2013/03/23/2977593.html 一.概念 反射就是把Java的各种成分映射成 ...

  7. Java反射机制DOME

    Java反射机制 public class TestHibernate { @Test public void TestHb(){ try { Class cs = Class.forName(&qu ...

  8. 反射——Java反射机制

    反射概述 什么是反射? ①   反射的概念是由Smith在1982年首次提出的,主要指程序可以访问.检测和修改它本身状态或行为的一种能力. ②   JAVA反射机制是在运行状态中,对应任意一个类,都能 ...

  9. Java反射机制可以动态修改实例中final修饰的成员变量吗?

    问题:Java反射机制可以动态修改实例中final修饰的成员变量吗? 回答是分两种情况的. 1. 当final修饰的成员变量在定义的时候就初始化了值,那么java反射机制就已经不能动态修改它的值了. ...

  10. Java反射机制学习与研究

    Java反射机制:可以获取正在运行时的Java对象. 1.判断运行时对象对象所属的类. 2.判断运行时对象所具有的成员变量和方法. 3.还可以调用到private方法,改变private变量的值. S ...

随机推荐

  1. TreeSet源码解析笔记

    定义: TreeSet是一个有序的集合,它的作用是提供有序的Set集合.它继承了AbstractSet抽象类,实现了NavigableSet<E>,Cloneable,Serializab ...

  2. 嵌套循环结合修改IFS环境变量遍历文件数据中IFS的修改一致性

    以下这个脚本使用了两个不同的IFS值来解析数据.第一个IFS值解析出/etc/passwd文件中的单独的行.内部for循环接着将IFS值改为冒号,云溪你从/etc/passwd的行中解析出单独的值. ...

  3. 算法竞赛模板 KMP

    KMP算法图解: ① 首先,字符串“BBC ABCDAB ABCDABCDABDE”的第一个字符与搜索词“ABCDABD”的第一个字符,进行比较.因为B与A不匹配,所以搜索词后移一位. ② 因为B与A ...

  4. WPF gridcontrol 后台代码清除过滤筛选条件

    WPF 后台清除gridcontrol过滤筛选条件: ColumnName:列名 user_GridControl:gridcontrol控件名 user_GridControl.ClearColum ...

  5. 2018-8-9-win-消息

    title author date CreateTime categories win 消息 lindexi 2018-8-9 15:35:4 +0800 2018-2-13 17:23:3 +080 ...

  6. tomcat配置报错解决方法 The jre_home environment variable is not defined correctly

    tomcat配置的时候弹出错误,The jre_home environment variable is not defined correctly,难道jre环境变量配置不正确?但是我们又可以执行j ...

  7. Qt 【关于控件样式,鼠标进入、离开、点击】

    比如举以下这个例子: QPushButton * okBtn; okBtn->setstylesheet("QPushButton{border-image:url(:/image/h ...

  8. js 邮箱,手机电话验证

    var mail=/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/; var tel=/^(\d3,4|\d{3,4} ...

  9. 【NIO】IO与NIO的区别

    一.概念 NIO即New IO,这个库是在JDK1.4中才引入的.NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多.在Java API中提供了两套N ...

  10. Shell5

    sed文本编辑器(vim,notepad)非交互式的文本编辑器sed是逐行处理编辑器    sed [选项] ‘条件指令'  文件   #没有条件指令时,默认对全文所有进行修改sed的所有操作默认时在 ...