首页 > 代码库 > 【学习札记-类加载器】
【学习札记-类加载器】
个人学习整理,如有不足之处,请不吝指教。转载请注明:@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方法得到扩展类加载器
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 *
***************************************************************************
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。