package huang.de.wei; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class CompileClassLoader extends ClassLoader { //讀取一個文件的內容 private byte[] getBytes(String filename) throws IOException{ File file=new File(filename); long len=file.length(); byte[] raw=new byte[(int)len]; FileInputStream fin =new FileInputStream(file); //一次讀取class文件的全部二進制數據 int r =fin.read(raw); if(r!=len) throw new IOException("無法讀取全部文件:"+r+" != " +len); fin.close(); return raw; } //定義編譯指定的java文件的方法 private boolean compile(String javaFile) throws IOException{ System.out.println("CompileClassLoader正在編譯"+javaFile+"..."); //調用系統的javac命令 Process p=Runtime.getRuntime().exec("javac"+javaFile); //其他的線程都在等待這個線程完成 try { p.waitFor(); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println(e);; } //獲取javac線程的退出值 int ret =p.exitValue(); return ret ==0; } //重寫ClassLoader的findClass方法 protected Class<?> findClass(String name){ System.out.println("進入findClass內部"); Class clazz=null; //將包路徑中的點(.)替換成斜線(/)。 String fileStub=name.replace(".", "/"); String javaFilename="src/"+fileStub+"java"; String ClassFilename ="bin/"+fileStub+".class"; File javaFile=new File(javaFilename); File classFile=new File(ClassFilename); //當指定的java源文件存在,且class文件不存在,或者java源文件的 //修改時間比class文件修改時間晚時,重新編譯 if(javaFile.exists()&&(!classFile.exists())||javaFile.lastModified()>classFile.lastModified()) { //如果編譯失敗,或者該class文件不存在 try { if(!compile(javaFilename)||!classFile.exists()){ } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //如果class文件存在,系統負責將該文件轉化成class對象 if(classFile.exists()){ try { byte[] raw = getBytes(ClassFilename); //調用ClassLoader的defineClass方法將二進制數據轉換成class對象 clazz=defineClass(name,raw,0,raw.length); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //如果clazz為null,表明加載失敗,則拋出異常 if(clazz==null){ try { throw new ClassNotFoundException(name); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block System.out.println("找不到這個類"); System.out.println(e.toString());; System.out.println("找不到這個類"); } } return clazz; } //定義一個主方法 public static void main(String[] args) throws Exception{ /*//如果運行程序沒有參數,即沒有目標類 if(args.length<1){ System.out.println("缺少運行的目標類,請按如下格式運行java源文件:"); System.out.println("java CompileClassLoader ClassName"); } //第一個參數需要運行的類 String progClass=args[0]; //剩下的參數將作為運行目標類時的參數,所以將這些參數復制到新的數組中 String progArgs[]=new String[args.length-1]; System.arraycopy(args,1,progArgs,0,progArgs.length); CompileClassLoader ccl=new CompileClassLoader(); //加載需要運行的類 Class<?> clazz =ccl.loadClass(progClass); //加載需要運行的類的主方法 Method main=clazz.getMethod("main",(new String[0].getClass())); Object argspArray[] ={progArgs}; main.invoke(null, argspArray);*/ // 如果運行該程序時沒有參數,即沒有目標類 args = new String[]{"huang.de.wei.Hello","嘿嘿"}; // 第一個參數是需要運行的類 String progClass = args[0]; System.out.println(args[0]); // 剩下的參數將作為運行目標類時的參數, // 將這些參數復制到一個新數組中 String[] progArgs = new String[args.length-1]; System.out.println(progArgs); System.arraycopy(args , 1 , progArgs , 0 , progArgs.length); CompileClassLoader ccl = new CompileClassLoader(); // 加載需要運行的類 Class<?> clazz = ccl.findClass(progClass); // 獲取需要運行的類的主方法 Method main = clazz.getMethod("main" , (new String[0]).getClass()); Object[] argsArray = {progArgs}; main.invoke(null,argsArray); } }
被加載的類:
package huang.de.wei; public class Hello { public static void main(String[] args){ System.out.println("tes22t2"); for (String arg : args) { System.out.println("運行Hello的參數:" + arg); } } }
運行結果:
huang.de.wei.Hello
[Ljava.lang.String;@1fc4bec
進入findClass內部
tes22t2
運行Hello的參數:嘿嘿
如果把 Class<?> clazz = ccl.findClass(progClass);改成 Class<?> clazz = ccl.loadClass(progClass);則程序運行不會進入我們重寫的findClass方法內部,這時候遇到什么情況程序會進入findClass內部呢?其實Class<?> clazz = ccl.loadClass(progClass);這句的運行機制是這樣的,如果loadClass()方法找不到需要被加載的類(這個代碼示例是用huang.de.wei.Hello這個類當做被加載的類),則再到findClass()方法里面找。
如果
Class<?> clazz = ccl.findClass(progClass);
改為
Class<?> clazz = ccl.loadClass(progClass);
且:
args = new String[]{"huang.de.wei.Hello","嘿嘿"};
改為:
args = new String[]{"huang.de.wei.Hello2","嘿嘿"};//Hello2這個類不存在,程序用loadClass()方法找不到,就再調用findClass再找
運行結果如下:
huang.de.wei.Hello2
[Ljava.lang.String;@1fc4bec
進入findClass內部
找不到這個類
java.lang.ClassNotFoundException: huang.de.wei.Hello2
找不到這個類
Exception in thread "main" java.lang.NullPointerException
at huang.de.wei.CompileClassLoader.main(CompileClassLoader.java:136)
