一、类加载器简介
JAVA虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,,类加载器也是JAVA类,因为其他是JAVA类的类加载器本身也要被类加载其加载。显然第一个类加载器不能是Java类,这个是BootStrap。
public classClassLoaderTest {
public static void main(String[] args) {
System.out.println(ClassLoaderTest.class.getClassLoader().
getClass().getName());
//getClassLoader()获得加载器的名字
System.out.println(System.class.getClassLoader());
}
}
由例子可以看到ClassLoaderTest类是由AppClassLoader类加载器加载的,System类是由BootStrap类加载器加载的。 JVM内核启动的时候,BootStrap就已经被加载了,它是内嵌在JVM内核中的,不需要其他类加载器加载。 Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。
类名.class.getClassLoader().getClass().getName();获取类加载器名称。
二、类加载器的委托机制
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的一个类。如果类A中引用了类B(在A类中又用到了B类或继承了B类),java虚拟机将使用加载类A的类装载器来加载类B,还可以直接调用ClassLoader.loadClass()方法来制定某个类加载器去加载某个类,每个类加载器加载类时,又先委托给其上级类加载器。
类加载器是类的加载工具,主要有三种类加载器 :分别是BootStrap, ExtClassLoader 和 AppClassLoader。
有一道面试题,能不能自己写个类叫java.lang.System?
答案是不能,即使写了也不会被类加载器加载。类加载机制采用委托机制,这样可以保证父级类加载器优先,也就是总是使用父级类加载器能找到的类,结果就是总是优先使用Java系统自身提供的System类,而不会使用我们自己所写的System类。
三、自定义类加载器
自定义类加载器通常由三个步骤:
1.自定义的类加载器继承ClassLoader
2.覆盖父类中的findClass方法
3.defineClass方法用来得到class文件的字节码
类加载的高级问题分析:
编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果为WebAppClassloader,把MyServlet.class文件打jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误。把servlet.jar也放到ext目录中,问题解决了,打印的结果是ExtclassLoader。
错误分析:
在没有将MyServlet导出为jar包到jdk\jre\lib\ext之前,MyServlet以及其父类都是由WebAppClassLoader加载的。但是导出到jdk\jre\lib\ext目录以后,就由ExtClassLoader类加载器加载。由于HttpServlet不在ext目录下,所以无法加载到。而且MyServlet和HttpServlet是同一线程的,这也就意味着他们两个是由同一个类加载器加载的。ExtClassLoader类加载器加载不了,就会直接报错,而不会再交给WebAppClassLoader类加载器再加载。
解决方法:将HttpServlet所在的jar包拷贝到jdk\jre\lib\ext目录下,重新启动tomcat服务器,重新在浏览器中访问MyServlet就可以了。