Java类加载器
类加载器层次结构(由高到低)
1.引导类加载器 bootstrap class loader
作用:用来加载java的核心库JAVA_HOME/jre/lib/rt.jar,不继承自java.lang.ClassLoader
2.扩展类加载器 extensions class loader
作用:Java虚拟机的实现回提供一个扩展目录,该类加载器在此目录里面查找并加载java类
3.应用程序类加载器 application class loader
作用:一般java应用类都是由它来完成
4.自定义类加载器
作用:实现自己的类加载器,以满足一些特殊要求
继承java.lang.ClassLoader类
自定义文件加载器:
1 import java.io.ByteArrayOutputStream; 2 import java.io.FileInputStream; 3 import java.io.FileNotFoundException; 4 import java.io.IOException; 5 import java.io.InputStream; 6 7 /** 8 * 自定义文件类加载器 9 * 1.继承java.lang.ClassLoader 10 * @author Nicotine 11 * 12 */ 13 public class FileSystemClassLoader extends ClassLoader { 14 //com.sansan.test.User 在D:/test/读取 15 //文件路径 16 private String rootDir; 17 18 public FileSystemClassLoader(String rootDir) { 19 this.rootDir = rootDir; 20 } 21 @Override 22 protected Class<?> findClass(String name) throws ClassNotFoundException { 23 24 Class<?> c = findLoadedClass(name); 25 26 //先查看有没有加载过这个类,如果已经加载过了,则直接返回;如果没有,则加载新的类 27 if (null!=c) { 28 return c; 29 }else { 30 //双亲模式:获取父类加载器, 31 ClassLoader parent = this.getParent(); 32 try { 33 c = parent.getClass(); 34 } catch (Exception e) { 35 } 36 } 37 if (null!=c) { 38 return c; 39 }else { 40 byte[] classData = getClassData(name); 41 if (null==classData) { 42 throw new ClassNotFoundException(); 43 }else{ 44 c=defineClass(name, classData, 0,classData.length); 45 } 46 } 47 return c; 48 } 49 private byte[] getClassData(String classname) { 50 //源数据路径 51 String path = rootDir + "/"+classname.replace(".","/")+".class"; 52 //读取 53 //IOUtils,可以使用它将IO流的数据转换成字节数组 54 //选择流 55 InputStream is = null; 56 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 57 58 try { 59 is=new FileInputStream(path); 60 byte[] buffer = new byte[1024]; 61 int temp = 0; 62 //读取 63 while ((temp=is.read(buffer))!= -1) { 64 baos.write(buffer,0,temp); 65 } 66 return baos.toByteArray(); 67 } catch (FileNotFoundException e) { 68 e.printStackTrace(); 69 return null; 70 } catch (IOException e) { 71 e.printStackTrace(); 72 return null; 73 }finally{ 74 if (is!=null) { 75 try { 76 is.close(); 77 } catch (IOException e) { 78 e.printStackTrace(); 79 } 80 } 81 if (baos!=null) { 82 try { 83 baos.close(); 84 } catch (IOException e) { 85 e.printStackTrace(); 86 } 87 } 88 } 89 } 90 }
测试代码:
public class TestFileClass { public static void main(String[] args) throws ClassNotFoundException { FileSystemClassLoader loader = new FileSystemClassLoader("D:/test"); Class<?> c = loader.loadClass("com.sansan.test.HelloWorld"); System.out.println(c.hashCode()); } }
运行输出类的哈希值
java.lang.ClassLoader类
根据一个指定的类的名称,找到或者生成其对应的字节码,然后从这些字节码中定义一个java类,级java.lang.Class的一个实例
相关方法
getParent() 返回该类加载器的父类加载器
loadClass(String name) 加载名称为name的类,返回结果就是java.lang.Class类的实例
findClass(String name) 查找名称为name的类,返回结果就是java.lang.Class类的实例
findLoadedClass(String name) 查找名称为name的已经被加载过的类,返回结果就是java.lang.Class类的实例
defineClass(String name,byte[] b,int off,int len)把字节数组b中的内容转换成java类,返回结果就是java.lang.Class类的实例
resolveClass(Class<?> c)链接指定的Java类
一般采用代理模式
交给其它加载器来加载指定的类
双亲委托机制
父类加载器优先加载
为了保证java核心库的类型安全
这种机制就保证不会出现用户自己能定义java.lang.Object类的情况
双亲委托机制是代理模式的一种
并不是所有的类加载器都采用双亲委托机制
Tomcat服务器加载器也是用代理模式,所不同的是它首先尝试去加载某各类,如果找不到在代理给父类加载器,这与一般的加载器顺序相反