自定义类加载器需要继承 java.lang.classloader 并覆盖 findclass(string name) 方法,通过读取字节流并调用 defineclass() 完成类的定义。实现时通常先检查父加载器是否已加载该类,若未找到则从指定源(如数据库、网络或加密文件)获取字节数组,最后将字节数组转换为 class 对象。

如何重写 findClass 以支持非标准来源的类加载
在 findClass 中直接处理字节流的读取与转换,跳过默认的委托机制,适用于需要从特定路径或数据源加载类的场景。开发者只需提供具体的字节获取逻辑,即可让 JVM 识别新定义的类。

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
public class CustomClassLoader extends ClassLoader {
private String sourcePath;
public CustomClassLoader(String sourcePath) {
this.sourcePath = sourcePath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] classData = loadClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
}
return defineClass(name, classData, 0, classData.length);
} catch (Exception e) {
throw new ClassNotFoundException(e.toString());
}
}
private byte[] loadClassData(String name) throws Exception {
String path = sourcePath + name.replace('.', '/') + ".class";
InputStream is = getClass().getClassLoader().getResourceAsStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b;
while ((b = is.read()) != -1) {
baos.write(b);
}
return baos.toByteArray();
}
}为什么需要显式调用 defineClass 而不是返回 Class 对象
ClassLoader 的设计原则是将“查找类”和“定义类”分离。findClass 负责定位二进制数据,而 defineClass 负责将字节码解析为 JVM 内部的 Class 结构。这种分离允许子类灵活决定数据来源,同时保证类加载的安全性和一致性。
立即学习“Java免费学习笔记(深入)”;

public class Main {
public static void main(String[] args) throws Exception {
CustomClassLoader cl = new CustomClassLoader("/app/classes/");
Class<?> c = cl.loadClass("com.example.MyClass");
System.out.println(c.getClassLoader());
System.out.println(c.getSuperclass());
}
}自定义类加载器加载的类其父加载器是谁
通过 defineClass 加载的类,其父加载器默认为当前 ClassLoader 实例的父加载器(即创建该 CustomClassLoader 时传入的 parent)。如果未指定父加载器,则使用系统类加载器作为父加载器。这意味着自定义加载器加载的类仍然遵循双亲委派模型的层级关系,除非手动打破这一模型。


















