jvm中除了根类加载器(BootstrapClassLoader)之外的所有类加载器都是继承ClassLoader实现的; ClassLoader类中的关键方法:
Class loadClass(String name,boolean resolve):此方法时ClassLoader的入口点,根据指定名称来加载类,系统就是调用ClassLoader的该方法获取指定类对应的Class对象;Class findClass(String name):根据指定名称来查找类;Class defineClass(String name,byte[] b,int off,int len):此方法负责把指定类的字节码文件(即class文件)读入字节数组byte[]中,并把它转换成class对象;此方法在ClassLoader中时final的,不运行重写; 下面实现一个简单的类加载器: package test; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.Method; public class CompileClassLoader extends ClassLoader { /** * 重写classLoader的findClass方法 */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class clazz = null; //将包路径中的"."换成“/” String fileStub = name.replace(".", "/"); String javaFilename = fileStub+".java"; String classFilename = 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())){ try{ //编译失败,或者class文件不存在 if(!compile(javaFilename) || ! classFile.exists()){ throw new ClassNotFoundException("ClassNotFountException:"+javaFilename); } }catch(IOException e){ e.printStackTrace(); } } //如果Class文件存在,系统负责将该文件转成class对象 if(classFile.exists()){ try{ //将class文件的二进制数据读入数组 byte[] raw = getBytes(classFilename); //调用ClassLoader的defineClass方法将二进制数据转成Class对象 clazz = defineClass(name, raw, 0,raw.length); }catch(IOException e){ e.printStackTrace(); } } //如果clazz为null,表明加载失败,抛出异常 if(clazz == null){ throw new ClassNotFoundException(name); } return clazz; } //读取一个文件的内容 private byte[] getBytes(String classFilename)throws IOException { File file = new File(classFilename); long length = file.length(); byte[] raw = new byte[(int)length]; try(FileInputStream inputStream = new FileInputStream(file)){ //一次读取class文件的全部二进制数据 int read = inputStream.read(raw); if(read != length){ throw new IOException("无法读取全部文件:"+read+ " != "+ length); } return raw; } } private boolean compile(String javaFilename)throws IOException { System.out.println("CompileClassLoader:正在编译"+javaFilename+"...."); //调用系统的javac命令,Process常用来调用外部shell命令等 Process process = Runtime.getRuntime().exec("javac"+javaFilename); try{ //其它线程都等待这个线程完成 process.waitFor(); }catch(InterruptedException e){ System.out.println(e); } //获取javac线程的退出值 int value = process.exitValue(); return value==0; } public static void main(String[] args) throws Exception { //如果运行该程序时没有参数,即没有目标类 if(args.length<1){ System.out.println("缺少目标类"); } //第一个参数时需要运行的类 String progClass = args[0]; //剩下的作为运行目标类时的参数 String[] progArgs = new String[args.length-1]; System.arraycopy(args, 1, progArgs, 0, progArgs.length); CompileClassLoader classLoader = new CompileClassLoader(); //加载需要运行的类 Class<?> clazz = classLoader.loadClass(progClass); //获取需要运行类的主方法 Method main = clazz.getMethod("main", (new String[0].getClass())); Object argsArray[] = {progArgs}; main.invoke(null, argsArray); } }下面写一个测试类:
package test; public class test { public static void main(String[] args) { for(String s : args){ System.out.println(s); } } }//运行时指定要加载类的全限定类名(即包名+类名)
如: test.test 这是第一个参数 这是第二个参数 run 运行结果如下图: