• java反射机制以及应用


    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)一起使用。需要public的无参构造函数。

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

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

    (7)有参构造函数调用:

    Class c=Class.forName("com.chx.TestB");

    Constructor ct=c.getDeclaredConstructor(new Class[]{int.class,String.class});

    ct.setAccessible(true);//可调用private的构造函数。

    TestB b2=(TestB) ct.newInstance(1,"2");

    3.为什么能支持反射:由于方法区保存着类的信息,比如属性、方法、访问权限等等,类的信息以对象的形式存在着,叫做元对象。这给反射提供了土壤,让反射变为可能。

    二、应用

    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.模拟运行时异常,传入代码如下:

    运行结果如下:

    二、未完成版反射深拷贝(后续补完)

    /**
         * 用反射实现深拷贝,要求必须是同一个类。
         * 要求:
         * 1.不支持继承类深拷贝
         * 2.把a的值赋给b
         * 3.必须有get set方法
         *
         * @param
         * @param a 参数1
         * @param b 参数2
         * @return
         * @author chx
         * @date 2020/6/9 16:33
         */
        public static <T> void deepCopy(T a, T b) {
            //首先判断是否属于同一类,有两种方案,可以用instanceof
            // 或者通过getClass().getSuperclass()
            Class<?> aClassa = a.getClass();
            Class<?> aClassb = b.getClass();
    
            if (aClassa != aClassb) {
                try {
                    throw new Exception("请传入相同类型的参数进行拷贝");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return;
            }
    
            //匹配上了,开始反射深拷贝
            Field[] fields = aClassa.getDeclaredFields();
    
            for (int i = 0; i < fields.length; i++) {
                try {
                    String name = fields[i].getName();
                    Field field = aClassa.getDeclaredField(name);
    
                    if (field != null) {
                        //获取A的get方法
                        String methodNameA = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
                        Method method = aClassa.getMethod(methodNameA);
                        Object result = method.invoke(a);
                        //将A的值set进B中
                        String methodNameB = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
                        Method methodB = aClassb.getMethod(methodNameB, String.class);
                        methodB.invoke(b, result);
                    }
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
  • 相关阅读:
    SQLServer 系统表简介
    C# abstract 和 virtual 关键字
    Windows脚本 %~dp0的含义
    Windows脚本 批处理中能够使用的系统变量
    Windows脚本 Shift 命令
    Windows脚本 关于本机ARP静态绑定批处理文件讲解[绑定ipmac脚本详解]
    开学测试
    仓库管理系统
    读《人月神话》
    个人作业2
  • 原文地址:https://www.cnblogs.com/chxwkx/p/11364154.html
Copyright © 2020-2023  润新知