类加载器就是记载类的一个工具。
我们在java程序中用到一个类,首先java 虚拟机会把这个类的字节码从硬盘加载到内存里面来,在对它进行一些处理,处理完之后就是字节码。通常这个字节码会放在硬盘指定的目录下。
jvm里面有多个类加载器。系统默认有三个类加载器。每个加载器都负责加载特定位置的类。每个加载器都有自己加载的区域,他们分别是 BootStrap、extClassLoader、AppClassLoader。
类加载器他也是一个类,他也需要被加载,那么他是被谁加载的呢?
在java虚拟机内核中有一个和虚拟机并存的一个类加载器BootStrap,他不是java中的一个类,当java虚拟机启动时他就跟着启动了,他是用C++语言写的一段二进制代码。 所以他不需要加载器。
在Class类中有这样一个方法。getClassLoader()他是获取加载本类的类加载器返回一个ClassLoader 类。
ClassLoaderTest{
public static void main(String[] args){
// 获取ClassLoaderTest类的字节码,然后获取加载他的那个类加载器,
//通过获取类加载器的字节码,然后获取类加载器的名字。
syso:ClassLoaderTest.class.getClassLoader().getClass().getName();
//打印的类加载器是AppClassLoader也就是说这个类是由AppClassLoader加载的。
syso:System.class.getClassLoader().getClass().getName();
//打印结果为null因为,加载这个类的类加载器不是Java类而是之前说过的那个嵌套在java //虚拟机中的一个类加载器,所以获得不了他的字节码和他的名字。
}
}
java虚拟机中给我们提供了三个类加载器他们是由子父关系组成的。
下面的一段代码把他们的关系给打印出来了
//获取他的类加载器
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
//循环只要不是BootStrap就一直循环
while(loader !=null){
//打印当前类加载器的名字
syso:loader.getClass().getName();
//把当前类加载器改为他的爸爸。
loader = loader.getParent();
}
//完了最后打印最大的类加载器;
syso: loader;
以上打印结果是: AppClassLoader
ExtClassLoader
null;
这个体系就出来了。我们 可以很形象的吧AppClassLoader比喻成 孙子,ExtClassLoader比喻成爸爸,null比喻成爷爷。
AppClassLoader:加载的是我们写的ClassPath路径下指定的所有jar包 或目录
ExtClassLoader: 加载的是JDK里面jre\ext文件里面的jar里面的类。
null : 加载的是jre\lib里面的rt.jar包里面的类里面都是系统类。
如果在在多个目录下都有这个class 文件java虚拟机会使用哪个类加载器去加载呢?
这就涉及到了委托机制。
首先用当前线程的类加载器去加载线程中的第一个类。
在Thread中有这样一个方法可以指定类加载器setContextClassLoader(ClassLoader c1)
一旦指定 了该线程运行到A类就用这个加类载器去加载如果A类引用了B类,java虚拟机会用加载A类 的类加载器去加载B类。
还可以调用ClassLoader.loaderClass()方法来指定类加载器,去加载类;
每个类加载器加载类的时候先不加载会先委托给上级,然后上级有会委托给上级。最后也就是”爷爷“他上 面没人了,所以他就加载了,如果找不到文件,不会报异常,他会给自己的下一级,知道给了发起者为止。
如果发起者还是没有找到Class文件或者jar包的话那么就直接报异常了。
这时发起者才加载类,也就是说每个类加载器都会先让BootSraat加载,指定的类加载器是最后才加载类 的。
|
|