------- android培训、java培训、期待与您交流! ----------
类加载器
类加载器
1. java虚拟机中可以安装多个加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
2. 类加载器也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap。
3. java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类装载其对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载
类加载器之间的父子关系和管辖范围图:
类加载器的委托机制
1. 当java虚拟机要加载一个类时,到底派出那个类加载器去加载呢?
(1) 首先当前线程的类加载器去加载线程中的第一个类。
(2) 如果类A中引用了类B,java虚拟机将使用加载类A的类装载器来加载类B
(3) 还可以之间调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
2. 每个类加载器加载类时,又先委托给其上级类加载器。
(1) 当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不会再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?所以不会再找了
(2)对着类加载器的层次结构图和委托加载原理解释先前将ClassLoaderTest输出成jre/lib/ext目录下itcast.jar包中后,运行结果为ExtClassLoader的原因:ext是app的爸爸,他优先找,并找到了
自定义类加载器
原理: 继承ClassLoader,不要覆盖loadClass方法,这样可以保留原来的委托机制,只覆盖findClass方法,使用自己的方式去查找,得到Class文件之后通过defineClass方法(把一个字节数组变成一个Class后返回),获取Class对象。
说明:
1.对不带包名的ClassLoaderAttachment.class文件进行加密,加密结果存放到另外一个目录classlib文件夹下。
2.用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。
3.这时将CLASSPATH下的ClassLoaderAttachment.class文件删除,并在ClassLoaderTest类中调用自定义类加载器加载classlib下的ClassLoaderAttachment.class
4.有包名的类不能调用无包名的类
//需要加载的文件,这里要继承一个类,为了加密后,引用父类创建对象通过编译
//自定义类加载器测试类
//自定义类加载器,内部使用了加密算法(加密和解密调用的是一个方法)
一个类加载器的高级问题分析
注意:当有两个类A和B, A用到了B类时,类加载器会先加载A,然后用加载A的类加载器会去加载B。
编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果为WebAppClassloader。而当我们把MyServlet.class文件打jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误
原因:WebAppClassLoader是ExtClassLoader的子类,开始使用WebAppClassLoader加载MyServlet,再去加载HttpServlet,就是下面的红线部分,而当我们把MyServlet.class文件打jar包,放到ext目录中时,这时使用的是父级加载器ExtClassLoader加载,由于委托机制,只会从发起点结束,而不会去找子类加载,所以HttpServlet无法加载。