目录
一、类加载过程
运行阶段,JVM中的类加载器读取.class文件,在方法区中创建该类的Class对象用于反射操作,对象中保存了类的格式和方法的字节码指令 ,。如果该类的父类还没有被加载,那么会首先加载父类。
JVM运行时并不会一次性加载所有的类,而是根据需要动态请求类加载 。每个类加载器都有一个HashMap,记录<全限定类名,Class对象>,所以对同一个ClassLoader,二进制名称相同的类只会被加载一次。
BootstrapClassLoader(启动类加载器):最顶层的加载类,由 C++实现,用来加载JDK的核心类库(%JAVA_HOME%/lib目录下)。AppClassLoader(应用程序类加载器):负责加载当前应用中的所有自定义jar包和类(项目根目录下)。

二、双亲委派模型
对于同一个ClassLoader来说,同一个类只能被加载一次,但是不同的加载类可以加载同一个类,这就有类的重复加载问题 。双亲委派模型会先将类加载请求转发给父类加载器,确保了不会有重复加载问题。在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试去加载。
此外,双亲委派模型还能防止核心API被篡改 ,当项目中有多个String.class类时,委派BootstrapClassLoader加载JDK中的String类防止AppClassLoader加载自定义的String类。
类加载过程如下:
- 类加载时首先判断当前类是否被加载过,已经被加载的类会直接返回,否则才会尝试加载。
- 类加载器在进行类加载的时候,它首先不会自己去尝试加载这个类,而是把这类加载请求委派给父加载器去完成 (调用父加载器
loadClass()方法来加载类),这样的话,所有的请求最终都会传送到顶层的启动类加载器BootstrapClassLoader中。 - 类加载器加载时首先判断父类是否被加载,如果没有则先加载父类。
- 只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载(调用自己的
findClass()方法来加载类)。 - 如果子类加载器也无法加载这个类,那么它会抛出一个
ClassNotFoundException异常。
java
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
//首先,检查该类是否已经加载过
Class c = findLoadedClass(name);
if (c == null) {
//如果 c 为 null,则说明该类没有被加载过
long t0 = System.nanoTime();
try {
if (parent != null) {
//当父类的加载器不为空,则通过父类的loadClass来加载该类
c = parent.loadClass(name, false);
} else {
//当父类的加载器为空,则调用启动类加载器来加载该类
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//非空父类的类加载器无法找到相应的类,则抛出异常
}
if (c == null) {
//当父类加载器无法加载时,则调用findClass方法来加载该类
//用户可通过覆写该方法,来自定义类加载器
long t1 = System.nanoTime();
c = findClass(name);
//用于统计类加载器相关的信息
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
//对类进行link操作
resolveClass(c);
}
return c;
}
}
三、自定义类加载器
由于AppClassLoader只能加载项目内的类,如果我们想加载项目外的类,默认的ClassLoader就不能满足我们的需求了,所以需要定义自己的ClassLoader。
继承ClassLoader抽象类,重写以下方法:
findClass(String name):给定要加载的类的二进制名称,重写该方法后默认还是双亲委派机制先调用父加载器loadClass()方法加载,如果加载失败才调用该方法内的逻辑加载。loadClass(String name, boolean resolve):重写该方法后类加载器会首先尝试自己加载name类。