Java 如何查找和加载类,如何加载来自网络的字节码?
未标记
考查重点:类加载过程与字节码定义。类加载器查找字节码后定义、链接和初始化类;网络字节码先取得并验证来源,再由自定义加载器定义
本题目录
参考答案
核心回答
Java 查找类不是扫描整块磁盘,而是由相应类加载器根据类的二进制名称,在自己的查找范围内找到或生成字节码,再交给 JVM 定义类。 字节码可以来自目录、JAR、模块,也可以来自网络或运行时生成的数据。
以普通应用加载 com.example.Plugin 为例,默认的 ClassLoader.loadClass 大致按以下顺序处理:
- 检查当前加载器是否已经是这个名称对应类的发起加载器,是则返回已有的
Class。 - 尝试交给父加载器加载;父加载器引用为
null时,尝试虚拟机内置的启动加载器。 - 父加载器找不到时,再调用自己的
findClass。普通classpath场景可以把类名映射为com/example/Plugin.class,在配置的目录或JAR中查找;自定义加载器也可以采用别的来源。 - 得到字节码后,通过
defineClass交给JVM创建对应的类;后续还涉及链接,并在需要时初始化。
从网络加载,通常只需要自定义 ClassLoader,重写 findClass 来获取字节数组,再调用带类名的 defineClass,不必因此打破双亲委派。
加载、链接、初始化和双亲委派是什么关系?
它们描述的是不同问题:
- 加载:取得定义类的二进制数据,在
JVM中创建相应的运行时类和Class对象。 - 链接:包括验证、准备、解析。验证检查结构与字节码约束;准备建立静态字段并设置默认值;解析把符号引用确定为具体的类、字段、方法等目标。解析可以按需进行,不要求所有引用都在初始化前一次性解析完。
- 初始化:执行类的静态初始化逻辑,例如静态字段的显式初始化、静态代码块对应的
<clinit>。加载成功并不表示已经执行这些逻辑,loadClass本身也不等于初始化。 - 双亲委派:回答“优先让哪个加载器寻找和定义这个类”,是默认类加载器的协作策略,不是上述阶段中的一个阶段。
以 Java SE 25 的内置加载器为例,有启动、平台和应用加载器。JDK 9 引入模块系统后,不能继续把 JDK 8 的 rt.jar、扩展目录和扩展加载器结构原样套用;普通 classpath 查找也不能代表所有模块加载过程。
网络字节码怎样落到这个流程中?
假设应用需要从插件仓库加载 com.example.Plugin,可以这样设计:
- 入口仍调用加载器的
loadClass,让已加载检查和默认委派规则先工作。 - 在自定义
findClass中,将允许的类名映射到仓库资源地址,获取完整字节数组,并处理下载失败、超时和内容版本。 - 对收到的数据核对来源与完整性,再交给
defineClass。传入的类名需要与字节码声明匹配,Class 格式、版本和后续验证也必须满足运行时要求。 - 后续由
JVM按需要完成链接与初始化。插件引用的其他类,也需要通过适当的加载器找到;加载主类并不等于所有依赖都已齐全。
因此,HTTP 请求拿到 200 或文件名以 .class 结尾,都不足以说明加载成功。反过来,字节码验证通过主要说明满足 JVM 的相应约束,也不表示其中的业务逻辑可以无条件信任。
这里的 loadClass 负责组织流程,findClass 负责扩展查找,defineClass 负责从字节创建类。通常重写 findClass 就够了;只有需要改变父优先等委派规则时,才考虑定制 loadClass。
常见追问:同名类一定是同一个类型吗?
不是。运行时类型身份由“二进制名称 + 定义它的类加载器”共同确定。 两个独立加载器分别定义相同字节、相同名称的类,仍可能是两个不同类型,不能直接互相强制转换。
插件系统中通常让公共接口由双方都能委派到的公共加载器定义,再由各插件加载器定义自己的实现。这样既能隔离插件实现,又能通过共同接口交互。仅仅让两个接口文件的内容相同,不能代替共享同一个运行时类型。
参考资料
- 国内:JavaGuide:类加载器详解:采用加载器职责、已加载检查和双亲委派部分,解释查找顺序以及为什么通常扩展 findClass,而不是直接改 loadClass。
- 国内:JavaGuide:类加载过程详解:采用加载、链接、初始化的职责划分,说明取得字节码与执行静态初始化逻辑是两件事。
- 国内:JavaGuide:类文件结构详解:采用 Class 格式、类名和版本信息部分,解释网络下载成功后为什么仍需检查格式与运行时兼容性。
- 国外:Oracle:Java SE 25 ClassLoader API:采用默认 loadClass 顺序、自定义网络加载器说明与 defineClass 约束,明确三个方法各自承担的工作。
- 国外:Oracle:Java SE 25 虚拟机规范第 5 章:采用类创建、类型身份、链接时机和初始化规定,说明定义加载器的作用及解析可以延后发生的边界。


