注:此为个人学习笔记,内容可能有些混乱,仅供参考。
1、 概览
因为类加载器 Java 类如同其它的 Java 类一样,也是要由类加载器来加载的。一般来说,开发人员编写的类加载器的父类加载器是系统类加载器。类加载器通过这种方式组织起来,形成树状结构。树的根节点就是引导类加载器。
2、 系统提供的类加载器
主要有下面三个:
· 引导类加载器 (bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader 。
· 扩展类加载器 (extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
· 系统类加载器 (system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader() 来获取它。
代码段:
package xjt.classloader;
public class ClassLoaderTree {
public static void main(String[] args) {
ClassLoader loader = ClassLoaderTree. class .getClassLoader();
/**
* 第一个输出的是 ClassLoaderTree类的类加载器,即系统类加载器。
* 它是 sun.misc.Launcher$AppClassLoader类的实例;
* 第二个输出的是扩展类加载器,是 * sun.misc.Launcher$ExtClassLoader类的实例。
* 需要注意的是这里并没有输出引导类加载器,
* 这是由于有些 JDK 的实现对于 父类加载器是引导类加载器的情况,
* getParent()方法返回 null 。
* */
while (loader != null ) {
System. out .println(loader.toString());
loader = loader.getParent();
}
}
}
类加载器在尝试自己去查找某个类的字节代码并定义它时, 会先代理给其父类加载器,由父类加载器先去尝试加载这个类 ,依次类推。在介绍代理模式 背后的动机之前,首先需要说明一下 Java 虚拟机是 如何判定两个 Java 类是相同的 。Java 虚拟机不仅要看类的 全名是否相同 ,还要看加载此类的 类加载器是否一样 。只有两者都相同的情况,才认为两个类是相同的。
真正完成类的加载工作是通过调用 defineClass 来实现的;而启动类的加载过程是通过调用 loadClass 来实现的。
方法 loadClass() 抛出的是 java.lang.ClassNotFoundException 异常;
方法 defineClass() 抛出的是 java.lang.NoClassDefFoundError 异常。
遇到 ClassNotFoundException 和 NoClassDefFoundError 等异常的时候,应该检查抛出异常的类的类加载器和当前线程的上下文类加载器 。
Class.forNameClass.forName 的一个很常见的用法是在加载数据库驱动的时候。如 Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance() 用来加载 Apache Derby 数据库的驱动。是一个静态方法,同样可以用来加载类。
对于运行在 Java EE™容器中的 Web 应用来说,类加载器的实现方式与一般的 Java 应用 有所不同 。不同的 Web 容器的实现方式也会有所不同。以 Apache Tomcat 来说, 每个 Web 应用都有一个对应的类加载器实例 。该类加载器也使用代理模式,所不同的是它是 首先尝试去加载某个类 ,如果找不到再代理给父类加载器。这与一般类加载器的顺 序是相反的。这是 Java Servlet 规范中的推荐做法,其目的是使得 Web 应用自己的类的优先级高于 Web 容器提供的类 。这种代理模式的一个例外是: Java 核心库的类是不在查找范围之内 的。这也是为了保证 Java 核心库的类型安全。
7、 OSGi (Open Service Gateway Initiative ) 是 Java 上的动态模块系统。
OSGi 中的每个模块(bundle)都包含 Java 包和类。模块可以声明它所依赖的需要导入(import)的其它模块的 Java 包和类(通过 Import-Package ),也可以声明导出(export)自己的包和类,供其它模块使用(通过 Export-Package )。也就是说需要能够隐藏和共享一个模块中的某些 Java 包和类。这是通过 OSGi 特有的类加载器机制来实现的。 OSGi 中的每个模块都有对应的一个类加载器。