- 类加载器会找到一个类文件,并将其加载到 JVM 中,以便程序可以使用它。
- 类在第一次使用时加载,而不是在启动时,加载器每次都会定位字节码。
- 内置加载器形成一个父链(引导程序、平台、应用程序),向上委派。
- 自定义类加载器是 Minecraft 模组能够修补游戏类的原因,因为它们在加载时进行修补.
类加载器是 Java 运行时环境的一部分,负责查找类文件并将它们加载到 JVM 中,以便程序可以使用。类并非全部在启动时加载。JVM 第一次需要某个类时才加载它,而类加载器负责定位字节码、读取字节码并将其交给 JVM。Minecraft Java 版正是运行在这种机制之上.
类加载器实际上做什么
类加载器将类名转换为运行中的 JVM 内部可用的类。您请求一个类型,加载器定位到匹配的 .class 字节码,然后 JVM 在内存中定义该类型。
这个过程分为三个步骤。加载器找到一个命名类对应的字节码。JVM 验证并链接它。然后,该类就可以使用了。这就是为什么 Java 程序可以在编译时未提及的代码也能被引入的原因:加载器只需要类名就能找到它
装载器层级
类加载器以父子链的形式组织,大多数类加载器会首先询问其父类加载器。一个标准的应用程序有三个内置的加载器堆叠在一起。
| 加载器 | 加载内容 |
|---|---|
| 引导加载器 | 核心 Java 运行时类 |
| 平台加载器 | 核心之外的标准库模块 |
| 应用程序加载器 | classpath 中您程序自己的类 |
当应用程序加载器被请求加载一个类时,它通常会首先向上委托。父类加载器有机会加载它,只有当没有父类加载器能够加载时,子类加载器才会尝试。这被称为委托模型,它确保整个程序的核类型保持一致
班级身份和自定义加载器
一个类由其名称和定义它的加载器共同标识。两个加载器可以各自加载同名类,JVM 将它们视为两种不同的类型。这听起来可能只是一件小事,但却是类加载器功能强大的根本原因。
程序可以定义自己的类加载器。自定义加载器可以从任何地方读取字节码:一个 JAR 文件、网络、生成的字节数组,甚至是在加载过程中对其进行转换的字节码。这正是插件系统、应用服务器和模组框架加载主机程序在构建时未知的代码的方式。
为什么这对 Minecraft 模组很重要
模组的存在得益于自定义类加载。Minecraft 的模组加载器通过自己的类加载器启动游戏,以便在游戏类加载时看到和修改它们。字节码补丁正是发生在那个挂载点。
当一个加载器位于磁盘和 JVM 之间时,它可以在 JVM 定义类之前对其进行转换。模组可以重写游戏方法、注入新的行为或添加字段,而无需在磁盘上修改游戏的副本。JVM 最终运行的是补丁后的类。有关这部分如何融入更广阔的图景,请参阅什么是 Minecraft 模组加载器?。
常见问题解答
首次使用时,而不是在启动时。当你的代码首次访问一个类型时,JVM 会请求一个类加载器去查找并定义它。这被称为延迟加载。
加载器会请求其父加载器在它尝试加载类之前先加载该类。父链一直向上延伸到启动加载器,后者可以防止在相同名称下重复加载核心 Java 类型。
是的,如果不同的类加载器定义了它们。JVM 通过类的名称及其定义加载器来识别一个类,因此来自不同加载器的同名类是不同的类型。
为了在游戏类加载和内存补丁的过程中观察它们。自定义加载器可以在 JVM 定义类之前转换字节码,这正是模组无需在磁盘上编辑游戏就能修改游戏的方式。