Java反射访问私有字段必须先调用setAccessible(true),否则get()/set()会抛IllegalAccessException;需用getDeclaredField()而非getField(),且字段名严格匹配、注意类层次和模块限制。

Java 反射获取私有字段前必须调用 setAccessible(true)
不加这一步,getDeclaredField() 虽能拿到 Field 对象,但后续调用 get() 或 set() 会直接抛 IllegalAccessException。JVM 默认禁止访问非 public 成员,反射也不能绕过访问控制——除非显式声明“我知情且负责”。
常见错误现象:Exception in thread "main" java.lang.IllegalAccessException: Class xxx can not access a member of class yyy with modifiers "private"。
-
setAccessible(true)必须在get()/set()之前调用,对同一个Field实例只需一次 - 它只影响当前
Field实例,不影响其他反射对象或运行时的正常访问控制 - JDK 9+ 在模块系统下可能受
--illegal-access策略限制(如默认为deny),需启动参数配合或模块导出
用 getDeclaredField() 而不是 getField()
getField() 只返回 public 字段(包括父类继承的),完全忽略 private、protected、package-private 成员;而 getDeclaredField(String name) 才能定位到本类声明的任意字段,无论修饰符。
注意:它不搜索父类字段。如果目标字段在父类中且是 private,必须在父类上单独调用 getDeclaredField()。
立即学习“Java免费学习笔记(深入)”;
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 字段名拼写必须完全一致,包括大小写和下划线
- 若字段不存在,抛
NoSuchFieldException,建议用 try-catch 包裹 - 多个同名字段(如子类重声明)时,
getDeclaredField()返回本类定义的那个
获取值时传入正确的对象实例(或 null 表示静态字段)
Field.get(Object obj) 的参数 obj 是字段所属的实例。对非 static 字段,传 null 会抛 IllegalArgumentException;对 static 字段,传任意非 null 实例也行,但惯例传 null 更清晰。
示例场景:想读取某个 User 实例里的 private String password:
Field pwdField = user.getClass().getDeclaredField("password");
pwdField.setAccessible(true);
String pwd = (String) pwdField.get(user); // 这里必须传 user 实例
- 类型强转需谨慎:
get()返回Object,原始类型字段(如int)会自动装箱,但若字段是Integer却存了null,强转成int会 NPE - 若字段是 final 且已初始化,某些 JDK 版本下修改其值可能无效(尤其是字符串常量池或编译期常量)
- 并发环境下修改私有状态可能破坏对象封装契约,引发不可预期行为
批量获取私有字段时注意异常粒度和作用域
调用 getDeclaredFields() 返回所有声明字段(含 private),但不会递归父类。若需完整字段列表,得手动遍历类层次结构。
容易被忽略的一点:每个 Field 都要单独调用 setAccessible(true),不能对数组整体操作。
- 别用
fields[0].setAccessible(true)后就以为整个数组都可访问了 - 捕获
SecurityException:在安全管理器启用的环境(如 Applet 或某些容器)中,setAccessible(true)可能被拒绝 - Android 上 ART 虚拟机对反射限制更严,尤其在高版本 targetSdk 下,部分私有 API 会被屏蔽,
setAccessible(true)也可能静默失败
SecurityException 或 NoSuchFieldException,而不是只盯着 IllegalAccessException。

















