ITEEDU

Java Gossip: 简介 ClassLoader

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。

来撰写一个简单的程序测试类别加载器,首先写一个测试类别:

ClassLoaderTest.java
public class ClassLoaderTest {
	static {
		System.out.println("类别被载入...");
	}
}


将这个类别先放在Classpath的路径下,然后撰写以下的程序:

ClassPathDemo.java
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载入的。

这边只是对类别加载器作个简单的描述,想更深入了解类别加载器,可参考这篇 深入类别载入器。