关于Java利用反射实现动态运行一行或多行代码

 更新时间:2023年04月06日 09:29:31   作者:CrazyDragon_King  
这篇文章主要介绍了关于Java利用反射实现动态运行一行或多行代码,借鉴了别人的方法和书上的内容,最后将题目完成了,和大家一起分享以下解决方法,需要的朋友可以参考下

Talk is cheap, show me the code!

先来看代码:

public class TestEval {
	public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		long start = System.currentTimeMillis();
		eval("int sum = 0; for (int i = 0; i < 1000; i++){sum += i;} System.out.println(sum);");
		long end = System.currentTimeMillis();
		System.out.println("运行耗时:"+(end-start)+"ms");
	}
	
	public static void eval(String code) throws 
	IOException, 
	ClassNotFoundException, 
	InstantiationException, 
	IllegalAccessException, 
	NoSuchMethodException, 
	SecurityException, 
	IllegalArgumentException, 
	InvocationTargetException {
		//创建 eval.java 文件
		File file = new File("src/Eval.java");
		//创建缓冲输出流
		BufferedWriter buffer = new BufferedWriter(new FileWriter(file));
		//将 code 里面的内容写入 buffer 中
		buffer.write("public class Eval{\n"            //必须是 public class,否则无法访问。
				+ "public void test(){\n"
				+ code+"\n}"
				+"\n}");
		buffer.close();
		
		File parentFile = new File(file.getParent());
		//注意这里要补一个分隔符,不然会出错 File.pathSeparator
		//这可能是 src  和  src/ 表示意义的区别
		String parentPath = parentFile.getAbsolutePath()+File.separatorChar;
		
		//文件分隔符,只能在前面加,因为它是和平台有关的,
		//而这里我需要使用正斜杠,在windows平台下,默认是反斜杠
		parentPath = parentPath.replace('\\', '/');   //反斜杠要使用转义字符

		//Eval.class的位置,我的电脑上是 file:///F:/JavaProject/eval/src/
		//注意最后面的文件分隔符
		//创建一个 URL 数组
		String url = "file:///"+parentPath;  
		System.out.println("测试使用,查看输出文件路径:"+url);
		
		//需要自己编译Java文件,产生 class 文件
		//下面这段代码用于编译文件,这是比较简单的了。
		//这个绝对路径是Java源文件的路径,使用javac编译获取 .class文件
		Process p = Runtime.getRuntime().exec("javac -encoding utf-8 "+file.getAbsolutePath()); 
		InputStream compileError = p.getErrorStream();
		//下面javac 的编译信息,与在命令行中使用的输出结果一样,只是把错误信息放到了控制台进行输出,如果没有输出,代表编译成功。否则会有错误提示。
		byte[] b = new byte[1000];
		while (compileError.read(b) != -1) {
			System.out.println(new String(b));
		}
        compileError.close();
		
		URL[] urls = {new URL(url)};
		//以默认的 ClassLoader 作为父 ClassLoader, 创建 URLClassLoader
		URLClassLoader classLoader = new URLClassLoader(urls);
		Class<?> clazz = classLoader.loadClass("Eval");
		Object ob = clazz.newInstance();
		Method mtd = clazz.getMethod("test", new Class<?>[] {});  //使用 null 会有警告
		//执行方法!
		mtd.invoke(ob, new Object[] {});  //使用 null 会有警告
		classLoader.close();   //关闭 URLClassLoader 对象
		System.out.println("执行结束");
	}
}

代码不多,但是用到了疯狂Java上的不少知识,比如使用 URLClassLoader 加载
字节码文件,因为一开始我是想使用 Class.forName(),但是发现总是提示找不到class文件,这似乎是 它的限制。然后我就想到了使用 URLClassLoader 这样加载的话,就不会有这个限制了。疯狂 Java 上面的例子是加载一个 jar 包,我这里就是加载一个单个 字节码文件(.class 文件)。

简要说明以下步骤:

1.首先将eval() 函数里面的 代码端,利用字符串拼接放入一个方法中,在放入一个类中:

       //创建 eval.java 文件
		File file = new File("src/Eval.java");
		//创建缓冲输出流
		BufferedWriter buffer = new BufferedWriter(new FileWriter(file));
		//将 code 里面的内容写入 buffer 中
	    buffer.write("public class Eval{\n"            //必须是 public class,否则无法访问。
				+ "public void test(){\n"
				+ code+"\n}"
				+"\n}");
		buffer.close();

2.然后利用Runtime.getRuntim.exec() 调用 javac 进行编译,因为源文件 是无法使用的,这是一种比较简单的编译文件的方式,我还见到了更为复杂的方式,但是限于水平,就采用了这种最简单的方式了。

3.获取字节码文件后,就要加载这个类了,但是使用 Class.forName() 这个方法,似乎不能加载任意类,必须是项目里面存在的类才行,这一点我还没明白,转念又想到了 ClassLoader 似乎可以解决这个问题,就是用了 URLClassLoader ,因为它可以从网络或者本地其他地方加载 字节码 文件,这一点非常方便。

4.加载字节码文件以后,就进入了反射的知识了。

		//这里的url 是当前 字节码文件的路径,注意后面要带上文件分隔符 '/'
		URL[] urls = {new URL(url)};
		//以默认的 ClassLoader 作为父 ClassLoader, 创建 URLClassLoader
		URLClassLoader classLoader = new URLClassLoader(urls);
		Class<?> clazz = classLoader.loadClass("Eval");
		Object ob = clazz.newInstance();
		Method mtd = clazz.getMethod("test", new Class<?>[] {});  //使用 null 会有警告
		//执行方法!
		mtd.invoke(ob, new Object[] {});  //使用 null 会有警告
		classLoader.close();   //关闭 URLClassLoader 对象

5.然后就可以运行了,但是不知到为什么,一开始运行特别慢,居然要 5000+ms,后来第二天中午我修改了以下以后,居然变快了。

Java还是很有趣的,继续深入学习,会发现更多有趣的知识。
下面是运行截图:

运行截图

在这里插入图片描述

到此这篇关于关于Java利用反射实现动态运行一行或多行代码的文章就介绍到这了,更多相关Java反射实现动态运行代码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程

    在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程

    本文介绍了在Java中使用ModelMapper库简化Shapefile属性转JavaBean的过程,对比了原始的set方法和构造方法,展示了如何使用ModelMapper进行动态属性映射,从而减少手动编写转换代码的工作量,通过示例代码,展示了如何使用GeoTools读取Shapefile属性并将其转换为JavaBean对象
    2025-02-02
  • Java实现数据库图片上传功能详解

    Java实现数据库图片上传功能详解

    这篇文章主要为大家详细介绍了如何使用Java实现数据库图片上传功能,包含从数据库拿图片传递前端渲染,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-03-03
  • 图解Springboot集成七牛云并实现图片上传功能过程

    图解Springboot集成七牛云并实现图片上传功能过程

    在实际开发中 ,基本都会有应用到文件上传的场景,但随着或多或少的需求问题,之前有在springboot上用过七牛云实现图片上传,今天因为某些原因又重新使用了下七牛云因此想总结下七牛云
    2021-11-11
  • Java编程异常处理最佳实践【推荐】

    Java编程异常处理最佳实践【推荐】

    这篇文章主要介绍了Java编程异常处理最佳实践【推荐】,具有一定参考价值,需要的朋友可以了解下。
    2017-10-10
  • SpringBoot 集成 activiti的示例代码

    SpringBoot 集成 activiti的示例代码

    这篇文章主要介绍了SpringBoot 集成 activiti的示例代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • spring实现bean对象创建代码详解

    spring实现bean对象创建代码详解

    这篇文章主要介绍了spring实现bean对象创建代码详解,具有一定借鉴价值,需要的朋友可以参考下
    2017-12-12
  • IDEA中的.iml文件和.idea文件夹使用方式

    IDEA中的.iml文件和.idea文件夹使用方式

    这篇文章主要介绍了IDEA中的.iml文件和.idea文件夹使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • Springboot与vue实例讲解实现前后端分离的人事管理系统

    Springboot与vue实例讲解实现前后端分离的人事管理系统

    这篇文章主要介绍了如何用Java实现企业人事管理系统,文中采用springboot+vue实现前后端分离,感兴趣的小伙伴可以学习一下
    2022-06-06
  • Java封装、继承、多态三大特征的理解

    Java封装、继承、多态三大特征的理解

    封装、继承、多态三大特征是java中比较常用的,务必要掌握,下面给大家介绍Java封装、继承、多态三大特征的理解,有不清楚的朋友可以一起学习下
    2016-06-06
  • SpringBoot如何获取Get请求参数详解

    SpringBoot如何获取Get请求参数详解

    SpringBoot为我们封装了许多简便的获取请求参数的方法,下面这篇文章主要给大家介绍了关于SpringBoot如何获取Get请求参数的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-12-12

最新评论