ClassLoader的概念
Java虚拟机设计团队把类加载阶段种的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类,实现这个动作的代码模块称为“类加载器”。 类加载器是Java语言的一项重大创新,在类层次划分、热部署、代码加密等领域都有应用。
ClassLoader的类型
对Jvm虚拟机而言,ClassLoader就分为两类:
- Bootstrap ClassLoader(
启动类加载器) C/C++编写,是虚拟机的一部分 - 其他类加载器 在虚拟机外实现
从我们开发人员角度看,ClassLoader还可以划分为以下几类:
-
Bootstrap ClassLoader(
启动类加载器)C/C++编写,是虚拟机的一部分,用于加载指定的JDK核心类库,如:
java.lang、java.util等。它会加载$JAVA_HOME/jre/lib目录和-Xbootclasspath参数指定的目录中的类库(必须是虚拟机识别的,但仅按文件名识别)。 -
Extentions ClassLoader(
扩展类加载器)负责加载
$JAVA_HOME/jre/ext目录和系统变量java.ext.dir目录中指定的类库. 在代码中由Launcher$ExtClassLoader实现.通过它内部方法可以看出它的加载范围:private static File[] getExtDirs() { String var0 = System.getProperty("java.ext.dirs"); File[] var1; if (var0 != null) { StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator); int var3 = var2.countTokens(); var1 = new File[var3]; for(int var4 = 0; var4 < var3; ++var4) { var1[var4] = new File(var2.nextToken()); } } else { var1 = new File[0]; } return var1; } -
Application ClassLoader(
应用程序类加载器)负责加载当前程序的
classPath目录和系统属性java.class.path目录下的类库. 在代码中由Launcher$AppClassLoader实现,可通过ClassLoader.getSystemClassLoader获取,一般情况下它就是系统中默认的类加载器. -
Custom ClassLoader(
自定义类加载器)当系统提供的类加载器不满足我们的需求时,我们可以通过继承
ClassLoader来自定义类加载器
加载机制——双亲委派模式
ClassLoader查找类时采用的时双亲委派模式,简单来说有以下几步基本流程:
- 子加载器判断该class是否已经加载
- 如果没有则委托父加载器查找,找到则返回,没有则依次递归直到顶层
bootstrap classLoader bootstrap classLoader中查找是否已加载该class,有则返回,没有则依次向下查找,如果没找到则最后会交给最初委托的ClassLoader去查找.
双亲委派的实现
看看ClassLoader中loadClass()中的实现会很容易理解:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// 这里的parent不是父类,而是父加载器,未指定的情况下则为SystemClassLoader
if (parent != null) {
c = parent.loadClass(name, false);
} else {
// 因为BootStrap ClassLoader为C/C++实现,在Java中无法直接获取,所以parent为null
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
类加载器之间的关系(不是继承关系)

我们自定义ClassLoader时只需要继承java.lang.ClassLoader,复写findClass(),在该方法中获取字节码数组,再通过defineClass()把字节码数组转换为Class类实例.
为什么要使用双亲委派
-
避免类重复加载
-
安全性.像
Object、String之类的系统类,在虚拟机启动时就会被加载,避免了自定义的String无法替换系统String类.两个类名完全一致,且被同一个类加载器加载的类,虚拟机才会认为它们是同一个类.