有关类加载器的基本知识,请先看文章:http://blog.csdn.net/tangyangguang/article/details/8878302
1,类加载器概述
Java程序是由许多独立的类字节码文件(*.class)组成。一个应用程序可能直接或间接的涉及到成千上万个Class,如果一次性全部装入内存,无疑是时间和空间的浪费。所以JVM启动时只会加载核心的类字节码和必要的其他类字节码,用户自定义的类将会在第一次使用时加载,这叫做动态加载类字节码。而 类加载器就是用来加载类字节码文件到JVM中,以供程序使用的。
2,JVM中的自带的类加载器
- Bootstrap ClassLoader
- 启动类加载器,用c/c++实现的,它负责加载核心Class(即所有java.*开头的类)。
- 这个类加载器没有对应的Java类。
- Extension ClassLoader
- 扩展类加载器,它负责加载扩展的Class(存放在JRE的lib/ext/目录下的类)。
- 这个类加载器对应sun.misc.Launcher$AppClassLoader类。
- Application ClassLoader
- 应用程序类加载器,负责加载应用程序自身的类(CLASSPATH目录中的Class)。
- 这个类加载器对应sun.misc.Launcher$ExtClassLoader类。
Bootstrap ClassLoader是最顶级的类加载器,也是Extension ClassLoader的父“类加载器”,Extension ClassLoader是Application ClassLoader的父“类加载器”。如下图所示:
测试代码:
package cn.itcast;
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
System.out.println("当前的类加载器:" + cl);
while(cl != null){
cl = cl.getParent();
System.out.println("parent类加载器:" + cl);
}
}
}
执行结果:
当前的类加载器:sun.misc.Launcher$AppClassLoader@19821f
parent类加载器:sun.misc.Launcher$ExtClassLoader@addbf1
parent类加载器:nul
3,“ClassLoader”与“类加载器”的区别
我们平常说的ClassLoader实际上表示两个概念:一是“类加载器”概念;二是ClassLoader类,全名是java.lang.ClassLoader。java.lang.ClassLoader是JDK中的一个普通类(ClassLoader是一个抽象类),是用Java语言编写的, 我们可以自己写一个ClassLoader的子类,以扩展Java虚拟机加载类的方式。
4,类加载器使用委托模式进行类加载
类加载器使用委托模型来搜索类和资源。每个“类加载器”实例都有一个相关的父“类加载器”(parent)。 需要查找类或资源时,类加载器实例会先委托给其父类加载器进行加载,如果parent是null的话,则表示为Bootstrap类加载器,只有在上级类加载器没有找到的情况下,自己才会加载,这就避免我们重写一些系统类,来破坏系统的安全。
分析java.lang.ClassLoader的源码:
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
// 先从缓存中拿Class:
// 1, 如果这个Class之前被加载过,就会记录到缓存中,这里就会返回缓存的Class对象。
// 2, 如果这个Class之前没有被加载过,则这里返回null
Class c = findLoadedClass(name);
if (c == null) {
// 如果缓存中没有想要的Class对象,则进行加载
try {
// 如果parent不为null,则调用parent的loadClass进行加载
if (parent != null) {
c = parent.loadClass(name, false);
}
// 如果parent为null,则调用BootstrapClassLoader进行加载
else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// 如果仍然无法加载成功,则调用自身的findClass进行加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
从以上源码中可以得知:
1,类加载器使用委托模式进行类加载。
2,我们如果想自己写一个类加载器,则只需要重载findClass()这个方法。
5,规则细节
- 类加载器使用委托模式进行类加载。
- Class.getClassLoader()方法可以返回加载这个Class的ClassLoader,如果返回null,表示是Bootstrap ClassLoader。
- 如果类A引用了类B,不管是直接引用还是用Class.forName()引用,JVM就会找到加载类A的ClassLoader,并用这个ClassLoader来加载类B。
- 我们如果想自己写一个类加载器,则只需要重载findClass()这个方法。
- 在自定义的ClassLoader对象中,需要指定一个父对象;如果没有指定的话,系统自动指定ClassLoader.getSystemClassLoader()为父对象。
- 我们可以在自定义的ClassLoader中重写loadClass()方法,在实现代码中不采用委托模式进行类加载,但不推荐这样做。
- Class.forName()是加载类字节码并进行初始化(例如执行static代码块),如果之前加载过,则会使用缓存的Class。
- ClassLoader.loadClass()是加载类字节码,但不会进行初始化(例如不会执行static代码块),而是在第一次使用这个类时执行初始化。
- 如果使用同一个ClassLoader实例多次加载同一个Class(自己重写loadClass()方法可以实现这个效果),会抛java.lang.LinkageError错误。
(实验代码将写到后面的文章中)