类加载器
Java虚拟机设计团队有意把类加载阶段中的 “通过一个类的全限定名来获取描述该类的二进制字节流” 这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需的类。实现这个动作的代码被称为 “类加载器” (ClassLoader)
类与类加载器
类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远超类加载阶段
对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等
以JDK 8为例
| 名称 | 加载的类 | 说明 |
|---|---|---|
| Bootstrap ClassLoader(启动类加载器) | JAVA_HOME/jre/lib | 无法直接访问 |
| Extension ClassLoader(拓展类加载器) | JAVA_HOME/jre/lib/ext | 上级为Bootstrap,显示为null |
| Application ClassLoader(应用程序类加载器) | classpath | 上级为Extension |
| 自定义类加载器 | 自定义 | 上级为Application |
启动类加载器
可通过在控制台输入指令,使得类被启动类加器加载
拓展类加载器
如果classpath和JAVA_HOME/jre/lib/ext 下有同名类,加载时会使用拓展类加载器加载。当应用程序类加载器发现拓展类加载器已将该同名类加载过了,则不会再次加载
双亲委派机制
双亲委派模式,即调用类加载器ClassLoader 的 loadClass 方法时,查找类的规则
双亲委派机制之前,先观察一下以下代码能否正确运行:
//自己定义的一个 java.lang包
package java.lang;
public class String {
public static void main(String[] args) {
String s = new String();
System.out.println(s);
}
}复制代码
以上代码,编译没有任何问题,但是运行时,却报错:
为什么提示在java.lang.String类中找不到main方法呢,其实,问题的关键就在于类加载遵循双亲委派机制。
类加载器有以下这样的层次关系:
当一个类在加载的时候,都会先委派它的父加载器去加载,这样一层层的向上委派,直到最顶层的启动类加载器。如果顶层无法加载(即找不到对应的类),就会一层层的向下查找,直到找到为止。 这就是类的双亲委派机制。
破坏双亲委派模式
-
双亲委派模型的第一次“被破坏”其实发生在双亲委派模型出现之前——即JDK1.2面世以前的“远古”时代
- 建议用户重写findClass()方法,在类加载器中的loadClass()方法中也会调用该方法
-
双亲委派模型的第二次“被破坏”是由这个模型自身的缺陷导致的
- 如果有基础类型又要调用回用户的代码,此时也会破坏双亲委派模式
-
双亲委派模型的第三次“被破坏”是由于用户对程序动态性的追求而导致的
- 这里所说的“动态性”指的是一些非常“热”门的名词:代码热替换(Hot Swap)、模块热部署(Hot Deployment)等