一、什么是类加载器
Java类加载器是Java运行时环境的一部分,负责动态加载Java类到虚拟机内存空间中。所有类被载入内存中都会生成一个java.lang.Class实例。同一个类不会被多次载入,一个类用全限定类名和其类加载器作为唯一标识。
二、类加载器种类
类加载器层次结构
- Bootstrap ClassLoader:负责加载核心库,如rt.jar等。包含JVM内部核心类(如:java.lang.*),不加载自定义Java类。
- Extension ClassLoader:加载扩展类(通过位于jre/lib/ext目录下)。
- Application ClassLoader:(System ClassLoader):加载应用程序类路径下的类(由classpath指定)。
- Custom ClassLoader: 用户自定义的类加载器,通常用于加载指定目录或模块下的类文件。
三、类加载器执行流程
加载(Loading):类加载器读取类字节码文件,将其转换为内存中的Class对象。
链接(Linking):将类的符号引用转为直接引用,验证字节码文件、分配类的静态变量及初始化、解析类的符号引用转为直接引用。
初始化(Initialization):执行静态初始化器及静态代码块,完成类初始化工作。
- 检查缓存,类加载会检查缓存区是否存在Class对象,ClassLoader在类加载器内部维护了一个已加载的Map。
- 委派给父类加载器,依次委派给父类加载器,确保系统核心类由上层加载器加载。
- 依次判断使用根类加载器、扩展类加载器、系统类加载器、自定义加载器进行Class对象加载。
- 返回Class对象,加载成功的Class对象存放在堆内存(Heap Memory)中,类信息和常量池存储在方法区(元空间)中。

四、双亲委派机制
JVM中类加载采用双亲委派机制,所谓双亲委派机制是指当一个类加载器收到类加载请求时不会直接去加载目标类,而是把该请求委托给父类加载器去加载。避免核心库被自定义加载器加载,也避免类被重复记载。

什么情况下会破坏双亲委派机制?
(1)线程上下文加载器(Thread Context ClassLoader)如果创建线程是未设置,则会父线程中继承一个。通过设置线程的上下文加载器,可以实现类的加载。当线程需要加载类时首先会使用其关联的上下文类加载器。
(2)自定义类加载器会绕过双亲委派。
五、自定义类加载器
(1)继承ClassLoader类
(2)重写findClass方法
(3)读取类数据
(4)调用defineClass方法
(5)返回类对象
public class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadClassData(name);
return defineClass(name, data, 0, data.length);
} catch (IOException e) {
throw new ClassNotFoundException("Could not load class " + name, e);
}
}
private byte[] loadClassData(String name) throws IOException {
String path = classPath + name.replace('.', '/') + ".class";
InputStream is = new FileInputStream(path);
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
int nextValue = 0;
while ((nextValue = is.read()) != -1) {
byteStream.write(nextValue);
}
return byteStream.toByteArray();
}
}