Reflection(反射,java.lang.reflect)是在运行时获取类、字段、方法、构造器的信息并调用它们的机制。
常用 API
Class.forName、getDeclaredField、getDeclaredMethod、getDeclaredConstructorField.get/Field.set、Method.invoke、Constructor.newInstancesetAccessible(true):关闭访问检查,访问 private 成员
模块系统下的限制
- Java 16 起默认强封装,Java 17 彻底强封装:对未开放(opens)的包调用
setAccessible(true)会抛InaccessibleObjectException。 - 可用
--add-opens启动参数放开,或借助 Unsafe、MethodHandle 中的IMPL_LOOKUP绕过。 static final字段即使setAccessible(true)也不能用Field.set修改;Java 12 起Field的modifiers等字段被反射过滤,旧的“去掉 final 修饰符”写法失效。
在模组中
- 運行時對於mc原版字段/方法需使用中間混淆名,或使用
ObfuscationReflectionHelper自动映射。 - 反射调用有额外开销,热点代码可改用 MethodHandle。
- 例如通过反射取得 ServerLevel 的
entityTickList,即可直接操作 EntityTickList。
Reflection is a Java mechanism to access classes, methods, fields and their metadata at runtime without knowing what their are.
Before JPMS you can use reflection to access nearly everything: unfortunately you can't do that anymore since Java9+. The module system introduced more strict access control, that class A from module A cannot access class B in module B if B isn't declared as "opened" in module B event if B is public.
Fortunately there are workarounds to fuck up module system's access control.
Commonly used APIs
Class.forName、getDeclaredField、getDeclaredMethod、getDeclaredConstructorTo obtain handles for class/field/method, Constructor can be considered an extension to Method.Field.get/Field.set、Method.invoke、Constructor.newInstanceTo actually invoke your handle.setAccessible(true)Tell JVM to ignore the private/protected access flag and do WTF you want.
Notes
getDeclaredField, getDeclaredMethod, getDeclaredConstructor etc. returns cached/copied handles that can be spoofed. To get the true handles you can invoke getDeclaredField0, getDeclaredMethod0, getDeclaredConstructor0, etc.
Reflection is known to be suck at performance as it does much work automatically like boxing/unboxing and type cast. MethodHandle/VarHandle will be a better alternative if you know exactly what you need to invoke.