类加载器
一、序言
类加载器(class loader)是Java中的一个很重要的概念。类加载器负责加载Java类的字节代码到Java虚拟机中。本文首先详细介绍了Java类加载器的基本概 念,包括代理模式、加载类的具体过程和线程上下文类加载器等,接着介绍如何开发自己的类加载器,最后介绍了类加载器在Web容器和OSGi中的应用。
类
加载器是Java语言的一个创新,也是Java语言流行的重要原因之一。它使得Java类可以被动态加载到Java虚拟机中并执行。类加载器从JDK
1.0就出现了,最初是为了满足Java Applet的需要而开发出来的。Java
Applet需要从远程下载Java类文件到浏览器中并执行。现在类加载器在Web容器和OSGi中得到了广泛的使用。
一般来说,Java应用的
开发人员不需要直接同类加载器进行交互。Java虚拟机默认的行为就已经足够满足大多数情况的需求了。不过如果遇到了需要与类加载器进行交互的情况,而对
类加载器的机制又不是很了解的话,就很容易花大量的时间去调试ClassNotFoundException和NoClassDefFoundError
等异常。
本文将详细介绍Java的类加载器,帮助读者深刻理解Java语言中的这个重要概念。
二、类加载器基本概念
顾名思义,类加载器(class
loader)用来加载Java类到Java虚拟机中。一般来说,Java虚拟机使用Java类的方式如下:Java源程序(.java文件)在经过
Java编译器编译之后就被转换成 Java字节代码(.class
文件)。类加载器负责读取Java字节代码,并转换成java.lang.Class类的一个实例。每个这样的实例用来表示一个Java类。通过此实例的
newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如Java字节代码可能是通过工具动态生成的,也可能是通过网络
下载的。(类加载器加载Java字节代码,而不关心这个字节代码的来源)。
基本上所有的类加载器都是java.lang.ClassLoader类的子类的实例。
2.1、java.lang.ClassLoader类介绍
java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出
一个Java类,即java.lang.Class类的一个实例。除此之外,ClassLoader还可以用来加载Java应用所需的资源,如图像文件和
配置文件等。不过本文只讨论其加载类的功能。
为了完成加载类的这个职责,ClassLoader提供了一系列的方法,比较重要的方法如表1所示。关于这些方法的细节会在下面进行介绍。
表1.ClassLoader中与加载类相关的方法
方法 | 说明 |
---|---|
getParent() | 返回该类加载器的父类加载器 |
loadClass(String name) | 加载名称为name的类,返回的结果是java.lang.Class类的实例 |
findClass(String name) | 查找名称为name的类,返回的结果是java.lang.Class类的实例 |
findLoadedClass(String name) | 查找名称为name的已经被加载过的类,返回的结果是java.lang.Class类的实例 |
defineClass(String name, byte[] b, int off, int len) | 把字节数组b中的内容转换成Java类,返回的结果是java.lang.Class类的实例。这个方法被声明为final的 |
resolveClass(Class c) | 链接指定的Java类 |
对于表1中给出的方法,表示类名称的name参数的值是类的二进制名称。需要注意的是内部类的表示,如com.example.Sample$1和com.example.Sample$Inner等表示方式。这些方法会在下面介绍类加载器的工作机制时,做进一步的说明。
2.2、类加载器的树状组织结构
Java中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由Java应用开发人员编写的。
2.2.1、系统提供的类加载器
1、引导类加载器(bootstrap class loader):它用来加载Java的核心库,是用原生代码来实现的,并不继承自java.lang.ClassLoader。
2、扩展类加载器(extensions class loader):它用来加载Java的扩展库。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载Java类。它继承自java.lang.ClassLoader。
3、系统类加载器(system class loader):它根据Java应用的类路径(CLASSPATH)来加载Java类。一般来说,Java应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader()来获取它。它继承自java.lang.ClassLoader。
备注:以上跟 Java中“路径”浅谈中的相关介绍一致。
2.2.2、Java应用开发人员编写的类加载器
除了系统提供的类加载器以外,开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
除
了引导类加载器之外,所有的类加载器都有一个父类加载器。通过表1中给出的getParent()方法可以得到。对于系统提供的类加载器来说,系统类加载
器的父类加载器是扩展类加载器,而扩展类加载器的父类加载器是引导类加载器;对于开发人员编写的类加载器来说,其父类加载器是加载此类加载器Java类的
类加载器。因为类加载器Java类如同其它的Java类一样,也是要由类加载器来加载的。一般来说,开发人员编写的类加载器的父类加载器是系统类加载器。
类加载器通过这种方式组织起来,形成树状结构。树的根节点就是引导类加载器。图1中给出了一个典型的类加载器树状组织结构示意图,其中的箭头指向的是父类加载器。
图1.类加载器树状组织结构示意图