Java 的ClassLoader
1. ClassLoader的用途
a: 类加载器(class loader)是用来动态的加载class文件到虚拟机当中 , 并转换成java.lang.Class类的一个实例 , 每个这样的实例用来表示一个java 类 , 因此我们可以根据 Class 的实例可以得到该类的信息 , 并通过实例的 newInstance()方法就可以创建出该类的一个对象 ,除此之外,ClassLoader还负责加载 Java 应用所需的资源,如图像文件和配置文件等。为了完成加载类的这个职责,ClassLoader提供了一系列的方法 ;
b: 场景描述 , 大家都知道,当我们写好一个Java程序之后,不是管是CS还是BS应用,都是由若干个.class文件组织而成的一个完整的Java应用程序,当程序在运行时,即会调用该程序的一个入口函数来调用系统的相关功能,而这些功能都被封装在不同的class文件当中,所以经常要从这个class文件中要调用另外一个class文件中的方法,如果另外一个文件不存在的,则会引发系统异常。
c: 程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被其它class所引用。
2.java默认提供的ClassLoad 和自定义ClassLoad
2-1: BootStrapClassLoader: 称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JAVA_HOME/jre/lib中JDK的核心类库,如:rt.jar、resources.jar、charsets.jar等 , 以及将 -Xbootclasspath 选项指定的jar包装入工作, Bootstrap ClassLoader不继承自ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。
2-2:ExtensionClassLoader:称为扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar 文件 , 以及将-Djava.ext.dirs 指定目录下的jar文件加载进去 (该类继承了java.lang.ClassLoader);
2-3:AppClassLoader: 称为系统类加载器,负责加载应用程序classpath目录下的所有jar文件和class文件 ,以及将-Djava.class.path所指的目录下的类与jar文件进行加载 , 一般来说,Java 应用的类都是由它来完成加载的 , 可以通过 ClassLoader.getSystemClassLoader()来获取它 (该类继承了java.lang.ClassLoader );
2-4:自定义类加载器:通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,用户自定义 ClassLoader 可以根据
用户的需要定制自己的类加载过程,在运行期进行指定类的动态实时加载。
3.ClassLoad 的层次结构和加载类的过程
3-1:classload 的层次结构


3-2:classload搜索类的过程
a: ClassLoader使用的是双亲委托模型来搜索类的,每个ClassLoader实例都有一个父类加载器的引用(不是继承关
系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其
它ClassLoader实例的的父类加载器。
b: 当Classloader在进行类加载时,首先会自底向上挨个检查是否已经加载了指定类,如果已经加载则直接返回该类
的引用。如果到最高层也没有加载过指定类,那么会自顶向下挨个尝试加载,如果高层加载器没有加载成功 , 高
层加载器会把加载任务转交给低层加载器进行加载 , 直到用户自定义类加载器,如果还不能成功,就会抛 出类没
有找到异常
3-3:classload 加载类的过程
类加载器会自顶向下挨个尝试加载,如果高层加载器没有加载成功 , 高层加载器会把加载任务转交给低层加载器
进行加载 , 直到用户自定义类加载器,如果还不能成功,就会抛 出类没有找到异常

3-4:测试两个类是否相同
JVM在判定两个class是否相同时,不仅要判断两个(包名+类名)是否相同,而且要判断是否由同一个类加载器实例加载的。只有两者同时满足的情况下,JVM才认为这两个class是相同的。就算两个class是同一份class字节码,如果被两个不同的ClassLoader实例所加载,JVM也会认为它们是两个不同class。比如网络上的一个Java类org.classloader.simple.NetClassLoaderSimple,javac编译之后生成字节码文件NetClassLoaderSimple.class,ClassLoaderA和ClassLoaderB这两个类加载器并读取了NetClassLoaderSimple.class文件,并分别定义出了java.lang.Class实例来表示这个类,对于JVM来说,它们是两个不同的实例对象,但它们确实是同一份字节码文件,如果试图将这个Class实例生成具体的对象进行转换时,就会抛运行时异常java.lang.ClassCaseException,提示这是两个不同的类型。现在通过实例来验证上述所描述的是否正确:
1) ; 在web服务器上建一个org.classloader.simple.NetClassLoaderSimple.java类
1. package org.classloader.simple;
2. public class NetClassLoaderSimple {
3. private NetClassLoaderSimple instance;
4. public void setNetClassLoaderSimple(Object obj) {
5. this.instance = (NetClassLoaderSimple)obj;
6. }
7. }
org.classloader.simple.NetClassLoaderSimple类的setNetClassLoaderSimple方法接收一个Object类型参数,并将它强制转换成org.classloader.simple.NetClassLoaderSimple类型。
2)、测试两个class是否相同(NetWorkClassLoader.java)
1. package classloader;
2. public class NewworkClassLoaderTest {
3. public static void main(String[] args) {
4. try {
5. //测试加载网络中的class文件
6. String rootUrl = "http://localhost:8080/httpw