# 第8章 运行时:加载与执行
# 一、前言
生成的 .class
如何在内存中变成可执行的 main
?本章用自定义 ClassLoader 与反射完成“定义类 → 获取 main → 调用”。
# 二、目标
- 理解双亲委派与 findClass 的职责
- 掌握反射调用
main(String[])
的参数传递细节 - 能在内存中加载并执行生成的类
# 三、设计
术语说明:
- 双亲委派:优先委派父加载器,找不到再由子加载器定义
- 反射:运行时获取方法并调用
核心流程图:
架构交互图:
# 四、实现
目录树(关注项):
src/main/java/com/lxg/runtime/InMemoryClassLoader.java
src/main/java/com/lxg/runtime/LxgShell.java
命令:
java -jar target/my-language-0.1.0-SNAPSHOT.jar examples/hello.lxg --emit-class=out/Program.class
java -jar target/my-language-0.1.0-SNAPSHOT.jar examples/hello.lxg
代码对照:内存类加载器(仅识别固定类名)
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.equals("com.lxg.gen.Program")) {
return defineClass(name, bytes, 0, bytes.length);
}
return super.findClass(name);
}
代码对照:反射调用 main
public void run(byte[] classBytes) {
try {
ClassLoader loader = new InMemoryClassLoader(classBytes);
Class<?> cls = loader.loadClass("com.lxg.gen.Program");
Method main = cls.getMethod("main", String[].class);
main.invoke(null, (Object) new String[0]);
} catch (Throwable t) {
t.printStackTrace(System.err);
throw new RuntimeException("Execution failed", t);
}
}
# 五、测试
- 端到端:
LxgEndToEndTest
即覆盖内存加载与反射路径 - 手动:修改示例后重复运行,观察输出变化
# 六、总结
- 运行时最小实现:单类加载 + 反射 main 调用;若需多类加载,可扩展为 Map<类名, 字节[]>