Java在需要使用类别的时候,才会将类别加载,Java的类别载入是由类别载入器(Class loader)来达到的,预设上,在程序启动之后,主要会有三个类别加载器:Bootstrap Loader、ExtClassLoader与AppClassLoader。
Bootstrap
Loader是由C++撰写而成,预设上它负责搜寻JRE所在目录的classes或lib目录下的.jar档案中(例如rt.jar)是否有指定的类别并加载(实际上是由系统参数sun.boot.class.path指定);预设上ExtClassLoader负责搜寻JRE所在目录的lib/ext 目录下的classes或.jar中是否有指定的类别并加载(实际上是由系统参数java.ext.dirs指定);AppClassLoader则搜寻 Classpath中是否有指定的classes并加载(由系统参数java.class.path指定)。
Bootstrap
Loader会在JVM启动之后载入,之后它会载入ExtClassLoader并将ExtClassLoader的parent设为Bootstrap Loader,然后BootstrapLoader再加载AppClassLoader,并将AppClassLoader的parent设定为 ExtClassLoader。
在加载类别时,每个类别加载器会先将加载类别的任务交由其parent,如果parent找不到,才由自己负责加载,如果自己也找不到,就会丢出 NoClassDefFoundError。
每一个类别被载入后,都会有一个Class的实例来代表它,每个Class的实例都会记得是哪个ClassLoader加载它的,可以由Class的getClassLoader()取得加载该类别的ClassLoader。
来撰写一个简单的程序测试类别加载器,首先写一个测试类别:
public class ClassLoaderTest { static { System.out.println("类别被载入..."); } }
将这个类别先放在Classpath的路径下,然后撰写以下的程序:
public class ClassLoaderDemo { public static void main(String[] args) { ClassLoaderTest test = new ClassLoaderTest(); ClassLoader loader = test.getClass().getClassLoader(); System.out.println(loader); System.out.println(loader.getParent()); System.out.println(loader.getParent().getParent()); } }
执行程序之后,会出现以下的讯息:
类别被载入... sun.misc.Launcher$AppClassLoader@82ba41 sun.misc.Launcher$ExtClassLoader@923e30 null
这表示ClassLoaderTest类别是由sun.misc.Launcher产生的AppClassLoader所加载,而 AppClassLoader的parent是ExtClassLoader(AppClassLoader与ExtClassLoader都是内部类 别),ExtClassLoader的parent则是Bootstrap Loader,最后一个显示null并不是表示没有ClassLoader,而是因为Bootstrap Loader是由C++撰写而成,在Java中并没有一个实际代表它的类别,因而没有类别实例来表示Bootstrap Loader。
如果把ClassLoaderTest的class档案放到JRE目录下的lib/ext/classes下,并重新执行程序,您会看到以下的讯息:
類別被載入... sun.misc.Launcher$ExtClassLoader@923e30 null Exception in thread "main" java.lang.NullPointerException at ClassLoaderDemo.main(ClassLoaderDemo.java:9)
这次ClassLoaderTest是由ExtClassLoader找到了,由于Bootstrap Loader并没有代表它的实例,所以是null,因而最后一行试图再使用getParent()时会出现NullPointException。
如果您将ClassLoaderTest的class档案移至JRE目录下的classes目录下,执行程序的话会出现以下的讯息:
類別被載入... null Exception in thread "main" java.lang.NullPointerException at ClassLoaderDemo.main(ClassLoaderDemo.java:8)
出现null讯息,表示这次ClassLoaderTest是由Bootstrap Loader载入的。
这边只是对类别加载器作个简单的描述,想更深入了解类别加载器,可参考这篇 深入类别载入器。