应重写 findclass 而非 loadclass,因 loadclass 已实现双亲委派逻辑,重写会破坏机制;findclass 专用于自定义字节码获取与 defineclass 定义,安全且标准。

Java 中自定义类加载器需继承 ClassLoader,并重写 findClass(String name) 方法,而不是 loadClass(String name) —— 因为后者已由父类实现双亲委派逻辑,直接重写会破坏机制。
为什么重写 findClass 而不是 loadClass
loadClass 是类加载的入口,内部默认先委托父加载器,失败后才调用 findClass。重写 findClass 可在不干扰委派链的前提下,专注“如何从源(如文件、网络、加密字节流)获取 class 字节码并定义类”。这是安全且标准的做法。
继承 ClassLoader 的基本结构
自定义加载器通常只需三步:
- 继承
java.lang.ClassLoader - 提供构造函数(常接受父加载器,显式调用
super(parent);若不传,默认使用当前线程上下文类加载器) - 重写
findClass(String name),在其中调用defineClass(byte[] b, int off, int len)定义类
一个典型实现示例
假设从指定目录加载 .class 文件:
public class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
public MyClassLoader(ClassLoader parent, String classPath) {
super(parent);
this.classPath = classPath;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
byte[] classBytes = loadClassBytes(name);
if (classBytes == null) {
throw new ClassNotFoundException(name);
}
// defineClass 将字节数组转为 Class 对象,并关联当前加载器
return defineClass(name, classBytes, 0, classBytes.length);
}
private byte[] loadClassBytes(String name) {
String path = classPath + File.separatorChar
+ name.replace('.', File.separatorChar) + ".class";
try (InputStream is = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
int b;
while ((b = is.read()) != -1) {
baos.write(b);
}
return baos.toByteArray();
} catch (IOException e) {
return null;
}
}
}
关键注意事项
- 不要在 findClass 中调用 loadClass:会导致无限递归或绕过委派机制
- defineClass 返回的 Class 对象,其类加载器自动设为当前实例:无需手动设置
-
类名必须用全限定名(如
com.example.MyClass),且路径中需将.替换为/或File.separator - 若需打破双亲委派(如热替换、隔离加载),可重写
loadClass,但务必谨慎处理委派逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











