个人学习整理,如有不足之处,请不吝指教。转载请注明:@CSU-Max
类加载器
简介
类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个
Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。
类加载器的分类
Java 中的类加载器的分类如下图,看图简洁明了:
启动类加载器(bootstrap class loader):加载 Java 的核心库。比如位于<JAVA_HOME>/jre/lib 目录下的vm.jar,core.jar。
扩展类加载器(extensions class loader):加载 Java 的扩展库。一般位于<JAVA_HOME>/jre/lib/ext 或者通过java.ext.dirs 这个系统属性指定的路径下的代码。这个类加载器是由sun.misc.Launcher$ExtClassLoader
实现的。
系统类加载器(system class loader):根据 Java 应用的类路径(CLASSPATH)来加载
Java 类。加载java.class.path(映射系统参数 CLASSPATH的值) 路径下面的代码,这个类加载器是由 sun.misc.Launcher$AppClassLoader 实现的。
类加载器的层次结构和双亲委派模型
分析
除了启动类加载器,所有的类加载器对象都有一个可以作为其双亲的类加载器对象。
通过 ClassLoader 类的 getParent 方法可以获取双亲类加载器对象。
我们自定义的类加载器 Java 类继承自 ClassLoader 类,而 ClassLoader 类的构造方法中可以指定类加载器的双亲类加载器对象,所以我们可以在自定义的类加载器的构造方法中调用父类 ClassLoader
类的构造方法,指定双亲类加载器对象的值。
若我们在自定义类加载器时没有指定双亲类加载器,则默认的双亲类加载器是系统类加载器。
若当前类加载器对象的双亲类加载器是启动类加载器,则其 getParent 方法返回 null。
下面是测试代码:
public void test()
{
ClassLoader cl = getClass().getClassLoader();
System.out.println(cl.toString());
if (cl != null)
{
cl = cl.getParent();
System.out.println(cl.toString());
}
}
测试结果:
sun.misc.Launcher$AppClassLoader@4d77c977 ---> 默认为系统类加载器
sun.misc.Launcher$ExtClassLoader@734bcb5c
---> getParent方法得到扩展类加载器
---> getParent方法返回null
通过以上的简单代码和输出结果可以很好的说明我们上面的说法。
根据以上的分析,我们可以发现这种加载的过程形成了一种层次结构,这种层次结构可以用如下简图表示:
没有自己定义的类加载器时 有自己定义的类加载器时
概念
一般应用程序都是由以上三种基本的类加载器加载的,当然,有时候也会使用我们自定义的类加载器。这些类加载器构成了一种层次结构,称为类加载器的双亲委派模型。
所有的类加载器(除了启动类加载器)对象都有一个可以作为其双亲的类加载器对象,通过组合关系来复用双亲类加载器的方法。
原理
当一个类加载器收到类加载请求时,它并不会第一时间自己去加载这个类,而是把该请求委派给双亲类加载器去完成,每个层次的类加载器都是如此(双亲类加载器再将请求委派给它的双亲类加载器),如果双亲类加载器可以完成类加载任务,就成功返回;只有当双亲类加载器无法完成该加载请求时(它的搜索范围内没有找到所需要的类),子类加载器才会自己去加载。(此处可以通俗的理解为递归)
实现
关于虚拟机中默认的双亲委派机制,我们可以查看 java.lang.ClassLoader 类的 loadClass
方法,源码及分析如下:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先判断请求的类是否已经被加载过了
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
//如果没有被加载,接着就检查双亲类加载器是否存在
try {
if (parent != null) {
//如果双亲类加载器存在,就调用其loadClass方法
c = parent.loadClass(name, false);
} else {
//如果不存在双亲类加载器,就检查是否使用启动类加载器加载该类
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
long t1 = System.nanoTime();
//如果依旧不能被加载,则调用自身的findClass来进行类加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
最近在看 java 虚拟机这一块,将自己的一些学习心得记录下来,分享给大家,多多指教。
***************************************************************************
* 转载请注明出处: @CSU-Max http://blog.csdn.net/csu_max
*
***************************************************************************
【学习札记-类加载器】,布布扣,bubuko.com
【学习札记-类加载器】
原文:http://blog.csdn.net/csu_max/article/details/26405341