概述: Java 17
FAQ for Java 17
Q: 利用反射机制给 private
属性的 Field
设置为 true
(field.setAccessible(true))时报: "java.lang.reflect.InaccessibleObjectException: Unable to make field private int java.io.StringReader.next accessible: module java.base does not "opens java.io" to unnamed module @411e7bd3
",如何解决?
问题描述
- 利用反射机制给
private
属性的Field
设置为true
(field.setAccessible(true)
)时报:
"
java.lang.reflect.InaccessibleObjectException: Unable to make field private int java.io.StringReader.next accessible: module java.base does not "opens java.io" to unnamed module @411e7bd3
"
问题分析
在 Java 17 中,由于模块系统的限制,直接使用反射机制访问私有属性可能会导致
InaccessibleObjectException
异常。
这是因为Java 9
引入的模块系统(Project Jigsaw
)限制了不同模块之间的非法反射访问。
解决方案
方法1. 使用 --add-opens
JVM 参数 (亲测有效)
可以通过在启动 Java 程序时添加 --add-opens
参数来允许特定模块的反射访问。例如,如果你需要访问 java.io
包中的私有字段,可以使用以下VM Option
参数:
--add-opens java.base/java.io=ALL-UNNAMED
这个参数允许所有未命名模块访问
java.base/java.io
包中的非公共成员。
方法2. 修改代码以使用 MethodHandles.privateLookupIn
- 在 Java 9 及以上版本中,可以使用
MethodHandles.privateLookupIn
方法来获取私有字段的访问权限。
以下是一个示例:
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;public class ReflectionExample {public static void main(String[] args) throws Exception {StringReader stringReader = new StringReader("example");// 获取私有字段的 VarHandleVarHandle nextField = MethodHandles.privateLookupIn(StringReader.class, MethodHandles.lookup()).findVarHandle(StringReader.class, "next", int.class);// 设置字段值nextField.set(stringReader, 10);System.out.println("Modified next field value: " + nextField.get(stringReader));}
}
方法3. 使用 Unsafe
类 (亲测有效)
- 虽然不推荐,但可以使用
Unsafe
类来绕过反射限制。
以下是一个示例:
import sun.misc.Unsafe;public class UnsafeExample {public static void main(String[] args) throws Exception {StringReader stringReader = new StringReader("example");// 获取 Unsafe 实例Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe) unsafeField.get(null);// 获取私有字段的偏移量Field nextField = StringReader.class.getDeclaredField("next");long offset = unsafe.objectFieldOffset(nextField);// 设置字段值unsafe.putInt(stringReader, offset, 10);System.out.println("Modified next field value: " + unsafe.getInt(stringReader, offset));}
}
总结
- 推荐使用
--add-opens
参数来解决反射访问私有字段的问题,因为它是最简单且符合Java
模块系统规范的解决方案。 - 如果需要更复杂的反射操作,可以考虑使用
MethodHandles.privateLookupIn
或Unsafe
类,但需要注意这些方法可能带来的安全性和稳定性问题。