标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过,JVM垃圾收集器可以回收这些Class对象
它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar或sun.boot.class.Path路径下的内容),是用原生代码来实现的,并不继承自java.lang.classloader。
加载扩展类和应用程序类加载器,并指定他们的父类加载器。
启动类加载器无法被Java程序直接引用从JDK1.2开始,java虚拟机规范推荐开发者使用双亲委派模式(ParentsDelegation Model)进行类加载,其加载过程如下: 1. 如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器去完成。 2. 每一层的类加载器都把类加载请求委派给父类加载器,直到所有的类加载请求都应该传递给顶层的启动类加载器。 3. 如果顶层的启动类加载器无法完成加载请求,子类加载器尝试去加载,如果连最初发起类加载请求的类加载器也无法完成加载请求时,将会抛出ClassNotFoundException,而不再调用其子类加载器去进行类加载。 双亲委派 模式的类加载机制的优点是java类它的类加载器一起具备了一种带优先级的层次关系,越是基础的类,越是被上层的类加载器进行加载,保证了java程序的稳定运行。
我们可以简单地自定义一个类加载器,用于加载某个class
public class FileSystemClassLoader extends ClassLoader { //文件的根目录 private String rootDir; public FileSystemClassLoader(String rootDir){ this.rootDir=rootDir; } //重写findClass方法 @Override protected Class<?> findClass(String s) throws ClassNotFoundException { Class c=findLoadedClass(s); if (c!=null){ return c; }else { ClassLoader parent=this.getParent(); //parent获取不到class时会抛出异常,为了继续执行使用try catch包裹 try{ c=parent.loadClass(s); }catch (Exception e){ } if (c!=null){ return c; }else { byte[] classData=getClassData(s); if (classData==null){ throw new ClassNotFoundException(); }else { //将字节数组转为Class c=defineClass(s,classData,0,classData.length); } } } return c; } //将文件转为字节数组 private byte[] getClassData(String className) { //改为文件地址 String path=rootDir+"/"+className.replace(".","/")+".class"; System.out.println(path); ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream(); InputStream inputStream=null; try { inputStream=new FileInputStream(path); byte[] buffer=new byte[1024]; int temp=0; while ((temp=inputStream.read(buffer))!=-1){ byteArrayOutputStream.write(buffer,0,temp); } return byteArrayOutputStream.toByteArray(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); return null; } finally { if (inputStream!=null){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (byteArrayOutputStream!=null){ try { byteArrayOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } }使用
public class UseCustomClassLoader { public static void main(String[]args){ FileSystemClassLoader loader=new FileSystemClassLoader("/home/xjk"); FileSystemClassLoader loader2=new FileSystemClassLoader("/home/xjk"); try { Class clazz1=loader.findClass("com.jk.bean.Emp");//本项目自定义的类调用AppClassLoader System.out.println(clazz1.getClassLoader()); Class clazz2=loader.findClass("java.lang.String");//rt.jar里的类调用BootstrapClassLoader System.out.println(clazz2.getClassLoader()); Class clazz3=loader.findClass("com.company.Main");//项目外的类调用自定义的FileSystemClassLoader System.out.println(clazz3.getClassLoader()); Class clazz4=loader2.findClass("com.company.Main");//使用不同类加载器,Class对象不一致 System.out.println(clazz4.getClassLoader()); System.out.println(clazz3==clazz4); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }输出结果
sun.misc.Launcher$AppClassLoader@18b4aac2 null com.jk.jvm.FileSystemClassLoader@1d44bcfa com.jk.jvm.FileSystemClassLoader@6f94fa3e false因为BootstrapClassLoader无法被Java程序直接引用,所以显示为空
