關(guān)于Java利用反射實(shí)現(xiàn)動(dòng)態(tài)運(yùn)行一行或多行代碼
Talk is cheap, show me the code!
先來(lái)看代碼:
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("運(yùn)行耗時(shí):"+(end-start)+"ms"); } public static void eval(String code) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { //創(chuàng)建 eval.java 文件 File file = new File("src/Eval.java"); //創(chuàng)建緩沖輸出流 BufferedWriter buffer = new BufferedWriter(new FileWriter(file)); //將 code 里面的內(nèi)容寫(xiě)入 buffer 中 buffer.write("public class Eval{\n" //必須是 public class,否則無(wú)法訪問(wèn)。 + "public void test(){\n" + code+"\n}" +"\n}"); buffer.close(); File parentFile = new File(file.getParent()); //注意這里要補(bǔ)一個(gè)分隔符,不然會(huì)出錯(cuò) File.pathSeparator //這可能是 src 和 src/ 表示意義的區(qū)別 String parentPath = parentFile.getAbsolutePath()+File.separatorChar; //文件分隔符,只能在前面加,因?yàn)樗呛推脚_(tái)有關(guān)的, //而這里我需要使用正斜杠,在windows平臺(tái)下,默認(rèn)是反斜杠 parentPath = parentPath.replace('\\', '/'); //反斜杠要使用轉(zhuǎn)義字符 //Eval.class的位置,我的電腦上是 file:///F:/JavaProject/eval/src/ //注意最后面的文件分隔符 //創(chuàng)建一個(gè) URL 數(shù)組 String url = "file:///"+parentPath; System.out.println("測(cè)試使用,查看輸出文件路徑:"+url); //需要自己編譯Java文件,產(chǎn)生 class 文件 //下面這段代碼用于編譯文件,這是比較簡(jiǎn)單的了。 //這個(gè)絕對(duì)路徑是Java源文件的路徑,使用javac編譯獲取 .class文件 Process p = Runtime.getRuntime().exec("javac -encoding utf-8 "+file.getAbsolutePath()); InputStream compileError = p.getErrorStream(); //下面javac 的編譯信息,與在命令行中使用的輸出結(jié)果一樣,只是把錯(cuò)誤信息放到了控制臺(tái)進(jìn)行輸出,如果沒(méi)有輸出,代表編譯成功。否則會(huì)有錯(cuò)誤提示。 byte[] b = new byte[1000]; while (compileError.read(b) != -1) { System.out.println(new String(b)); } compileError.close(); URL[] urls = {new URL(url)}; //以默認(rèn)的 ClassLoader 作為父 ClassLoader, 創(chuàng)建 URLClassLoader URLClassLoader classLoader = new URLClassLoader(urls); Class<?> clazz = classLoader.loadClass("Eval"); Object ob = clazz.newInstance(); Method mtd = clazz.getMethod("test", new Class<?>[] {}); //使用 null 會(huì)有警告 //執(zhí)行方法! mtd.invoke(ob, new Object[] {}); //使用 null 會(huì)有警告 classLoader.close(); //關(guān)閉 URLClassLoader 對(duì)象 System.out.println("執(zhí)行結(jié)束"); } }
代碼不多,但是用到了瘋狂Java上的不少知識(shí),比如使用 URLClassLoader 加載
字節(jié)碼文件,因?yàn)橐婚_(kāi)始我是想使用 Class.forName(),但是發(fā)現(xiàn)總是提示找不到class文件,這似乎是 它的限制。然后我就想到了使用 URLClassLoader 這樣加載的話,就不會(huì)有這個(gè)限制了。瘋狂 Java 上面的例子是加載一個(gè) jar 包,我這里就是加載一個(gè)單個(gè) 字節(jié)碼文件(.class 文件)。
簡(jiǎn)要說(shuō)明以下步驟:
1.首先將eval() 函數(shù)里面的 代碼端,利用字符串拼接放入一個(gè)方法中,在放入一個(gè)類中:
//創(chuàng)建 eval.java 文件 File file = new File("src/Eval.java"); //創(chuàng)建緩沖輸出流 BufferedWriter buffer = new BufferedWriter(new FileWriter(file)); //將 code 里面的內(nèi)容寫(xiě)入 buffer 中 buffer.write("public class Eval{\n" //必須是 public class,否則無(wú)法訪問(wèn)。 + "public void test(){\n" + code+"\n}" +"\n}"); buffer.close();
2.然后利用Runtime.getRuntim.exec()
調(diào)用 javac 進(jìn)行編譯,因?yàn)樵次募?是無(wú)法使用的,這是一種比較簡(jiǎn)單的編譯文件的方式,我還見(jiàn)到了更為復(fù)雜的方式,但是限于水平,就采用了這種最簡(jiǎn)單的方式了。
3.獲取字節(jié)碼文件后,就要加載這個(gè)類了,但是使用 Class.forName()
這個(gè)方法,似乎不能加載任意類,必須是項(xiàng)目里面存在的類才行,這一點(diǎn)我還沒(méi)明白,轉(zhuǎn)念又想到了 ClassLoader
似乎可以解決這個(gè)問(wèn)題,就是用了 URLClassLoader
,因?yàn)樗梢詮木W(wǎng)絡(luò)或者本地其他地方加載 字節(jié)碼 文件,這一點(diǎn)非常方便。
4.加載字節(jié)碼文件以后,就進(jìn)入了反射的知識(shí)了。
//這里的url 是當(dāng)前 字節(jié)碼文件的路徑,注意后面要帶上文件分隔符 '/' URL[] urls = {new URL(url)}; //以默認(rèn)的 ClassLoader 作為父 ClassLoader, 創(chuàng)建 URLClassLoader URLClassLoader classLoader = new URLClassLoader(urls); Class<?> clazz = classLoader.loadClass("Eval"); Object ob = clazz.newInstance(); Method mtd = clazz.getMethod("test", new Class<?>[] {}); //使用 null 會(huì)有警告 //執(zhí)行方法! mtd.invoke(ob, new Object[] {}); //使用 null 會(huì)有警告 classLoader.close(); //關(guān)閉 URLClassLoader 對(duì)象
5.然后就可以運(yùn)行了,但是不知到為什么,一開(kāi)始運(yùn)行特別慢,居然要 5000+ms,后來(lái)第二天中午我修改了以下以后,居然變快了。
Java還是很有趣的,繼續(xù)深入學(xué)習(xí),會(huì)發(fā)現(xiàn)更多有趣的知識(shí)。
下面是運(yùn)行截圖:
到此這篇關(guān)于關(guān)于Java利用反射實(shí)現(xiàn)動(dòng)態(tài)運(yùn)行一行或多行代碼的文章就介紹到這了,更多相關(guān)Java反射實(shí)現(xiàn)動(dòng)態(tài)運(yùn)行代碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot+Netty+WebSocket實(shí)現(xiàn)消息發(fā)送的示例代碼
這篇文章主要介紹了SpringBoot+Netty+WebSocket實(shí)現(xiàn)消息發(fā)送的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09SpringBoot消息國(guó)際化配置實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了SpringBoot消息國(guó)際化配置實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07Delegate IDE build/run actions to maven 配置會(huì)影響程序運(yùn)行嗎?
這篇文章主要介紹了Delegate IDE build/run actions to maven 配置會(huì)影響程序運(yùn)行嗎,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08Javacv使用ffmpeg實(shí)現(xiàn)音視頻同步播放
這篇文章主要介紹了Javacv使用ffmpeg實(shí)現(xiàn)音視頻同步播放,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12解決HttpServletResponse和HttpServletRequest取值的2個(gè)坑
這篇文章主要介紹了解決HttpServletResponse和HttpServletRequest取值的2個(gè)坑問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12Java實(shí)現(xiàn)批量操作Excel的示例詳解
在操作Excel的場(chǎng)景中,通常會(huì)有一些針對(duì)Excel的批量操作,以GcExcel為例,為大家詳細(xì)介紹一下Java是如何實(shí)現(xiàn)批量操作Excel的,需要的可以參考一下2023-07-07