java类如何加载
Java 类的加载机制
Java 类的加载过程由 类加载器(ClassLoader) 完成,遵循 双亲委派模型,确保类的唯一性和安全性。类加载分为 加载、链接、初始化 三个阶段。
类加载的三个阶段
加载(Loading)
类加载器读取 .class 文件,将其二进制数据加载到内存中,并在方法区生成对应的 Class 对象。
- 通过全限定名获取类的二进制流。
- 将静态存储结构转换为方法区的运行时数据结构。
- 生成
java.lang.Class对象作为访问入口。
链接(Linking)
链接阶段分为三个子步骤:
- 验证(Verification):检查字节码是否符合 JVM 规范(如魔数、语法、安全性)。
- 准备(Preparation):为类变量(静态变量)分配内存并设置默认初始值(如
int初始化为0)。 - 解析(Resolution):将符号引用(如类名、方法名)转换为直接引用(内存地址)。
初始化(Initialization)
执行类构造器 <clinit>() 方法,完成静态变量的显式赋值和静态代码块的执行。
- 触发条件:
new、静态方法/字段访问、反射调用等。 - JVM 保证多线程环境下初始化过程的同步。
类加载器分类
Java 类加载器分为以下层级:
-
Bootstrap ClassLoader(启动类加载器)

- 由 C++ 实现,加载
JAVA_HOME/lib下的核心类库(如rt.jar)。 - 是唯一没有父加载器的加载器。
- 由 C++ 实现,加载
-
Extension ClassLoader(扩展类加载器)
- 加载
JAVA_HOME/lib/ext目录或java.ext.dirs指定的扩展类。
- 加载
-
Application ClassLoader(应用类加载器)
- 加载用户类路径(
classpath)下的类,是默认的类加载器。
- 加载用户类路径(
-
自定义 ClassLoader
- 继承
ClassLoader,重写findClass()方法实现自定义加载逻辑(如热部署、加密类文件)。
- 继承
双亲委派模型
类加载器按层级委派加载请求:

- 收到加载请求后,优先委托父加载器尝试加载。
- 父加载器无法完成时(如目标类不在其搜索范围),子加载器才自行加载。
优点:
- 避免重复加载,确保核心类库的安全性(如防止用户伪造
java.lang.String)。 - 破坏双亲委派的场景:
- SPI 服务加载(如 JDBC 使用
Thread.currentThread().getContextClassLoader())。 - 热部署或模块化需求(如 OSGi、Tomcat 的 WebAppClassLoader)。
- SPI 服务加载(如 JDBC 使用
自定义类加载器示例
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;
}
}
常见问题与场景
何时触发类加载?
- 主动引用(如
new、访问静态字段/方法)会触发初始化。 - 被动引用(如通过子类访问父类静态字段)不会触发子类初始化。
如何打破双亲委派?
- 重写
loadClass()方法(不推荐,破坏安全性)。 - 使用线程上下文类加载器(如 JDBC 驱动加载)。
类卸载条件
- 类的
Class对象无引用。 - 加载该类的
ClassLoader实例无引用。 - 卸载通常发生在自定义类加载器场景(如热部署)。
通过理解类加载机制,可以更好地设计模块化应用或解决类冲突问题(如依赖冲突)。






