在 Java 中,类加载器(ClassLoader) 是 JVM 的重要组成部分,负责将 .class
文件加载到 JVM 中,并生成对应的 Class
对象。类加载器在 Java 的运行时环境中起着至关重要的作用,它决定了类如何被加载、链接和初始化。
Java 类加载器的层次结构:
Java 中的类加载器采用双亲委派模型(Parent Delegation Model),其层次结构如下:
- Bootstrap ClassLoader(启动类加载器):
- 最顶层的类加载器,由 JVM 实现,通常用本地代码(如 C++)编写。
- 负责加载 JVM 核心类库(如
java.lang.*
、java.util.*
等),这些类位于JAVA_HOME/lib
目录下。 - 是唯一没有父类加载器的类加载器。
- Extension ClassLoader(扩展类加载器):
- 由
sun.misc.Launcher$ExtClassLoader
实现。 - 负责加载
JAVA_HOME/lib/ext
目录下的类库,或由java.ext.dirs
系统变量指定的路径中的类。 - 父类加载器是 Bootstrap ClassLoader。
- 由
- Application ClassLoader(应用程序类加载器):
- 由
sun.misc.Launcher$AppClassLoader
实现。 - 负责加载用户类路径(ClassPath)下的类库,即应用程序中的类。
- 父类加载器是 Extension ClassLoader。
- 由
- 自定义类加载器:
- 开发者可以继承
java.lang.ClassLoader
类,实现自定义的类加载器。 - 自定义类加载器通常用于加载非标准路径下的类,或实现类的动态加载、热部署等功能。
- 开发者可以继承
双亲委派模型:
双亲委派模型是类加载器的工作机制,其核心思想是:
- 当一个类加载器收到类加载请求时,它首先不会尝试自己加载,而是将请求委派给父类加载器。
- 每一层的类加载器都会依次向上委派,直到启动类加载器。
- 如果父类加载器无法加载该类,子类加载器才会尝试自己加载。
双亲委派模型的优点:
- 避免重复加载:确保一个类只会被加载一次,防止类的重复加载。
- 安全性:防止核心类库被篡改,确保 Java 核心类库的安全性。
- 隔离性:不同的类加载器可以加载不同来源的类,实现类的隔离。
类加载的过程:
类加载的过程分为以下三个阶段:
- 加载(Loading):
- 通过类的全限定名获取类的二进制字节流。
- 将字节流转换为方法区中的运行时数据结构。
- 生成一个代表该类的
Class
对象。
- 链接(Linking):
- 验证(Verification):确保字节码文件的正确性和安全性。
- 准备(Preparation):为类的静态变量分配内存并设置默认初始值。
- 解析(Resolution):将符号引用转换为直接引用。
- 初始化(Initialization):
- 执行类的静态初始化代码(如静态代码块和静态变量的赋值)。
示例代码:
public class ClassLoaderExample {
public static void main(String[] args) {
// 获取应用程序类加载器
ClassLoader appClassLoader = ClassLoaderExample.class.getClassLoader();
System.out.println("Application ClassLoader: " + appClassLoader);
// 获取扩展类加载器
ClassLoader extClassLoader = appClassLoader.getParent();
System.out.println("Extension ClassLoader: " + extClassLoader);
// 获取启动类加载器(通常为 null,因为由 JVM 实现)
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println("Bootstrap ClassLoader: " + bootstrapClassLoader);
}
}
自定义类加载器:
public class CustomClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 自定义加载逻辑,例如从网络或文件中加载类
byte[] classData = loadClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
}
return defineClass(name, classData, 0, classData.length);
}
private byte[] loadClassData(String className) {
// 实现类的加载逻辑
return null; // 返回类的字节码数据
}
}
注意事项:
- 打破双亲委派模型:在某些场景下(如 Tomcat 的类加载器),可能需要打破双亲委派模型,以实现类的隔离和动态加载。
- 类加载器的隔离:不同的类加载器加载的类是不同的,即使它们的全限定名相同。
通过理解类加载器及其工作机制,可以更好地掌握 Java 的类加载过程、类隔离机制以及动态加载技术。
THE END
暂无评论内容