java如何加载类
Java 类加载机制
Java 类加载是通过类加载器(ClassLoader)实现的,主要分为以下几个阶段:
加载(Loading) 从文件系统、网络或其他来源加载类的字节码文件(.class),并生成对应的 Class 对象。类加载器会先检查是否已加载过该类,避免重复加载。
验证(Verification) 确保加载的字节码符合 Java 虚拟机规范,防止恶意代码破坏运行时环境。验证内容包括文件格式、元数据、字节码指令等。
准备(Preparation) 为类的静态变量分配内存并设置默认初始值(如 int 默认为 0,引用类型默认为 null)。此时不会执行任何 Java 代码。
解析(Resolution) 将常量池中的符号引用转换为直接引用(如将类名、方法名等解析为具体的内存地址)。
初始化(Initialization) 执行类的静态代码块(static {})和静态变量赋值操作。这是类加载的最后阶段,JVM 会确保一个类在多线程环境下只被初始化一次。
类加载器类型
Bootstrap ClassLoader 由 JVM 实现,负责加载核心 Java 类库(如 rt.jar、charsets.jar 等),通常用 null 表示。
Extension ClassLoader 加载 JRE 扩展目录($JAVA_HOME/lib/ext 或 java.ext.dirs 指定目录)中的类。
Application ClassLoader 加载用户类路径(ClassPath)上的类,也称为系统类加载器,通过 ClassLoader.getSystemClassLoader() 获取。
自定义 ClassLoader 用户可继承 ClassLoader 类实现自定义加载逻辑,常见于热部署、模块化等场景。
双亲委派模型
类加载器在加载类时会先委托父加载器尝试加载,只有父加载器无法完成时才自己加载。这种机制保证了核心类库的安全性,避免用户代码覆盖 Java 核心类。
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {}
if (c == null) {
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
自定义类加载示例
以下代码展示如何实现一个简单的自定义类加载器:
public class CustomClassLoader extends ClassLoader {
private String classPath;
public CustomClassLoader(String classPath) {
this.classPath = classPath;
}
@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) {
String path = classPath + File.separatorChar +
className.replace('.', File.separatorChar) + ".class";
try (InputStream is = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
return baos.toByteArray();
} catch (IOException e) {
return null;
}
}
}
动态加载类
通过 Class.forName() 方法可以动态加载类并初始化:
Class<?> clazz = Class.forName("com.example.MyClass");
如果不需要初始化,可指定 initialize 参数为 false:
Class<?> clazz = Class.forName("com.example.MyClass", false, classLoader);
常见问题
NoClassDefFoundError 类在编译时存在但运行时找不到,通常由类路径配置错误引起。
ClassNotFoundException 尝试通过字符串名称加载类但找不到对应类文件。
LinkageError 类加载过程中验证失败或版本不兼容导致。
破坏双亲委派 某些框架(如 Tomcat、OSGi)会修改加载顺序以实现模块隔离或热部署功能。







