您好,登錄后才能下訂單哦!
這篇文章主要介紹“Java怎么利用反射實現動態運行一行或多行代碼”,在日常操作中,相信很多人在Java怎么利用反射實現動態運行一行或多行代碼問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”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怎么利用反射實現動態運行一行或多行代碼”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。