Field.setAccessible(true)并非突破private修饰符本身,而是将Field的override标志设为true,使后续get()/set()跳过JVM运行时访问检查(包级、继承、模块、SecurityManager及caller-sensitive校验),不改字节码、不删private,仅临时关闭反射路径的安全校验。

Field.setAccessible(true) 并不是真正“突破”private修饰符本身,而是让 JVM 在反射调用路径上跳过对访问修饰符的运行时检查。它不改字节码、不删private关键字,只是临时关闭反射层面的安全校验。
核心机制:绕过运行时访问检查
Java 的 private 限制在编译期和类加载期并不强制执行;真正的权限判断发生在反射调用(如 field.get() 或 field.set())时,由 AccessController 和 ReflectionFactory 协同完成。调用 setAccessible(true) 会将该 Field 对象的 override 标志设为 true,后续反射操作便不再触发 checkMemberAccess 流程。
- 这属于 JVM 内部反射机制的“开关”,底层依赖 Unsafe 的内存操作能力
- 它只影响反射 API 调用路径,不影响正常编译调用(private 方法仍不能被普通代码直接调用)
- 安全管理器(SecurityManager)若启用,仍可能拦截该操作(但 JDK 17+ 已默认移除)
实际修改私有字段的必要步骤
要真正读写 private 字段,仅调用 setAccessible(true) 不够,必须配合正确的反射调用流程:
- 用 getDeclaredField("fieldName") 获取字段对象(getField() 只查 public,无效)
- 调用 field.setAccessible(true) 启用反射访问
- 对实例字段,传入目标对象调用 field.set(obj, value);静态字段则传 null
- 基本类型优先用 setInt()、setBoolean() 等专用方法,避免装箱异常或静默失败
JDK 9+ 模块系统带来的硬性拦截
即使 setAccessible(true) 成功,模块系统(Module System)仍可能直接抛出 InaccessibleObjectException。这不是反射失效,而是 JVM 主动阻止跨模块穿透:
立即学习“Java免费学习笔记(深入)”;
- 例如访问 java.base 中的 private 字段(如 System.lineSeparator),默认被禁止
- 需通过 JVM 参数显式开放:--add-opens java.base/java.lang=ALL-UNNAMED
- 若目标类在你自己的模块中,应在 module-info.java 中声明:opens com.your.pkg;
final 字段的特殊限制
private final 字段表面可 setAccessible,但修改往往无效:
- JDK 12+ 默认禁止修改 final 字段,即使 setAccessible(true) 也触发 IllegalAccessException
- 强行修改需先用反射修改 Field 自身的 modifiers 字段(去掉 Modifier.FINAL 位),但这属于高危操作
- JVM 可能已对该字段做常量内联优化,运行时修改不会反映在所有读取点


















