9.1 类加载器概述

Java中的类加载器并不是以特殊的方式实现的。类加载器本身也是Java类。Java标准库中的java.lang.ClassLoader类是所有由Java代码创建的类加载器的父类。通过调用类加载器中的loadClass方法可以加载Java类。由于类加载器本身也是Java类,因此类加载器自身的Java类需要由另外的类加载器来加载。注意,类加载器只有自身被加载到虚拟机中之后才能加载其他的Java类。这似乎是一个无法解决的循环问题。实际上,Java平台提供了一个启动类加载器(bootstrap class loader),由原生代码来实现。启动类加载器负责加载Java自身的核心类到虚拟机中。在启动类加载器完成初始的加载工作之后,其他继承自ClassLoader类的类加载器可以正常工作。

一个Java类被加载之后,可以通过其对应的Class类的对象的getClassLoader方法来获取加载它的类加载器的对象。如果通过继承ClassLoader类实现自己的类加载器,那么可以直接创建出类加载器类的对象。ClassLoader类提供的方法比较多,除了加载Java类之外,还能加载相关的资源文件,比如图片和属性文件等。ClassLoader类中的一部分方法是声明为受保护的,只能在子类中使用。这些受保护的方法主要是为创建自定义的类加载器而提供的。在使用ClassLoader类的对象来加载Java类时,使用loadClass方法即可。该方法的参数是Java类的二进制名称,返回值是表示该Java类的Class类的对象。代码清单9-1给出了ClassLoader类的loadClass方法的使用示例。在代码中使用当前Java类的类加载器来加载java.lang.String类。通过这种方式加载的Class类的对象与直接使用java.lang.String.class是一样的。

代码清单9-1 ClassLoader类的loadClass方法的使用示例


public void loadClass()throws Exception{

ClassLoader current=getClass().getClassLoader();

Class<?>clazz=current.loadClass("java.lang.String");

Object str=clazz.newInstance();

System.out.println(str.getClass());//输出java.lang.String

}


Java平台上的类加载器大概可以分成两类:启动类加载器和用户自定义的类加载器。两者的区别在于启动类加载器是由原生代码实现的,而用户自定义的类加载器继承自ClassLoader类。在用户自定义的类加载器中,一部分类加载器由Java平台默认提供,另外一部分由程序自己创建。Java平台默认提供的用户自定义类加载器有两个:第一个是扩展类加载器(extension class loader),用来从特定的路径加载Java平台的扩展库;第二个是系统类加载器(system class loader),又称应用类加载器(application class loader),它的作用是根据应用程序运行时的类路径(CLASSPATH)来加载Java类。如果程序中没有使用其他自定义的类加载器,则程序本身的Java类都由系统类加载器负责加载。通过ClassLoader类的静态方法getSystemClassLoader可以得到系统类加载器对象。通过系统类加载器对象的getParent方法可以得到扩展类加载器对象。

前面提到过,类加载器的根本作用是从字节代码中定义出表示Java类的Class类的对象。这个定义过程由ClassLoader类中的defineClass方法来实现。如果一个Java类是由某个类加载器对象的defineClass方法定义的,则称这个类加载器对象是该Java类的定义类加载器(defining class loader)。当使用类加载器对象的loadClass方法来加载一个Java类时,称这个类加载器对象为该Java类的初始类加载器(initiating class loader)。一个Java类的初始类加载器和定义类加载器不一定相同。初始类加载器可能把实际的加载工作代理给其他类加载器,由后者完成定义Java类的工作。初始类加载器和定义类加载器之间的关系是:一个Java类的定义类加载器是该类所引用的其他Java类的初始类加载器。在通过调用类加载器对象的loadClass方法进行类的加载,并成功得到一个Class类的对象之后,虚拟机会把类加载器对象和它加载的Class类的对象之间的关联记录下来。在加载Java类时,虚拟机会先检查是否存在与当前类加载器对象关联在一起的名称相同的Class类的对象。如果存在,那么直接使用该Class类的对象,而不会重新进行加载。