24.2 理解ClassLoader
24.2 理解ClassLoader
类ClassLoader是一个抽象类,Application ClassLoader和ExtensionClassLoader的具体实现类分别是sun.misc.Launcher$AppClassLoader和sun.misc.Launcher$ExtClassLoader, Bootstrap ClassLoader不是由Java实现的,没有对应的类。
每个Class对象都有一个方法,可以获取实际加载它的ClassLoader,方法是:
1 | public ClassLoader getClassLoader() |
ClassLoader有一个方法,可以获取它的父ClassLoader:
1 | public final ClassLoader getParent() |
如果ClassLoader是Bootstrap ClassLoader,返回值为null。比如:
1 | public class ClassLoaderDemo { |
输出为:
1 | sun.misc.Launcher$AppClassLoader |
ClassLoader有一个静态方法,可以获取默认的系统类加载器:
1 | public static ClassLoader getSystemClassLoader() |
ClassLoader中有一个主要方法,用于加载类:
1 | public Class<? > loadClass(String name) throws ClassNotFoundException |
比如:
1 | ClassLoader cl = ClassLoader.getSystemClassLoader(); |
需要说明的是,由于委派机制,Class的getClassLoader方法返回的不一定是调用load-Class的ClassLoader,比如,上面代码中,java.util.ArrayList实际由BootStrap ClassLoader加载,所以返回值就是null。
在反射一章,我们介绍过Class的两个静态方法forName:
1 | public static Class<? > forName(String className) |
第一个方法使用系统类加载器加载,第二个方法指定ClassLoader,参数initialize表示加载后是否执行类的初始化代码(如static语句块),没有指定默认为true。
ClassLoader的loadClass方法与Class的forName方法都可以加载类,它们有什么不同呢?基本是一样的,不过,ClassLoader的loadClass不会执行类的初始化代码,看个例子:
1 | public class CLInitDemo { |
使用ClassLoader加载静态内部类Hello, Hello有一个static语句块,输出”hello”,运行该程序,类被加载了,但没有任何输出,即static语句块没有被执行。如果将loadClass的语句换为:
1 | Class<? > cls = Class.forName(className); |
则static语句块会被执行,屏幕将输出”hello”。
我们来看下ClassLoader的loadClass代码,以进一步理解其行为:
1 | public Class<? > loadClass(String name) throws ClassNotFoundException { |
它调用了另一个loadClass方法,其主要代码为(省略了一些代码,加了注释,以便于理解):
1 | protected Class<? > loadClass(String name, boolean resolve) |
参数resolve类似Class.forName中的参数initialize,可以看出,其默认值为false,即使通过自定义ClassLoader重写loadClass,设置resolve为true,它调用父ClassLoader的时候,传递的也是固定的false。findClass是一个protected方法,类ClassLoader的默认实现就是抛出ClassNotFoundException,子类应该重写该方法,实现自己的加载逻辑,后文我们会给出具体例子。