[Java] Java 17 FAQ
概述: 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");
// 获取私有字段的 VarHandle
VarHandle 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));
}
}
- 案例实践
import sun.misc.Unsafe;
import java.io.StringReader;
import java.lang.reflect.Field;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SourceStringReader extends StringReader {
...
/**
* 获取 StringReader 的 next(游标位置/指针)
* @description 利用反射原理,将 java.io.StringReader 的 private 属性 next 读取出来
* @return
*/
@SneakyThrows
public int next(){
int next = Integer.MIN_VALUE; //读取失败时,以此值为标志
//反射方法1 : Java 17 中需结合 VM Option 参数 : `--add-opens java.base/java.io=ALL-UNNAMED`
//java.lang.reflect.Field field = java.io.StringReader.class.getDeclaredField("next");
//field.setAccessible(true);
//next = field.getInt( this );//读取 next 的值
////field.set(this, Integer.MIN_VALUE);//设置字段的值
//反射方法2: 基于 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);
next = unsafe.getInt(this, offset);
//unsafe.putInt(stringReader, offset, 10);// 设置字段值
return next;
}
...
}
总结
- 推荐使用
--add-opens参数来解决反射访问私有字段的问题,因为它是最简单且符合Java模块系统规范的解决方案。 - 如果需要更复杂的反射操作,可以考虑使用
MethodHandles.privateLookupIn或Unsafe类,但需要注意这些方法可能带来的安全性和稳定性问题。
X 参考文献
本文作者:
千千寰宇
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!

浙公网安备 33010602011771号