methodhandles.lookup 严格遵循 java 访问控制规则,仅在查找阶段校验权限,调用时不重复检查;获取字段句柄需满足语言可见性、模块读取权限和类加载器一致性三重约束,私有字段仅能通过本类 lookup 或 privatelookupin(配合 --add-opens)合法访问。

MethodHandles.Lookup 不直接“控制”变量成员的访问权限,而是严格遵循并执行 Java 的访问控制规则——它本身不放宽、不绕过、也不修改权限,只是在查找阶段做一次精准校验,之后调用时不再重复检查。
Lookup 对字段访问的权限边界很明确
要通过 Lookup 获取字段句柄(如 FieldHandle),必须满足:当前 Lookup 实例所属类与目标字段所在类处于同一“可访问域”。这个域由三重约束共同决定:
- 语言层可见性:private 字段只能被本类声明的 Lookup 访问;protected/package-private 字段需在同一包或子类关系下;public 字段才对 publicLookup() 可见
- 模块层读取权限:若目标类在命名模块中,调用方模块必须已声明 reads 该模块,否则即使同包也会失败(Java 9+)
- 类加载器一致性:Lookup 实例和目标类通常需由同一类加载器加载;跨加载器场景下需额外验证保护域(ProtectionDomain)
获取私有字段句柄的合法方式只有两种
不能靠 setAccessible(true) 那套反射逻辑,也不能用 publicLookup() 强行 in() 到别的类。真正合规的操作是:
- 本类内访问本类私有字段:直接用 MethodHandles.lookup().findVarHandle(Owner.class, "fieldName", FieldType.class)
- 跨类访问私有字段(如测试/框架场景):使用 MethodHandles.privateLookupIn(TargetClass.class, lookup),前提是 lookup 所在类已被 JVM 授权(例如模块已用 --add-opens 显式开放)
注意:privateLookupIn 不是万能钥匙,它仍会校验调用者是否具备模块级打开权限;若未配置,抛出 IllegalAccessException 而非 NoSuchFieldException。
字段句柄调用无需再检查权限,但类型必须精确
一旦拿到 VarHandle(或 unreflectGetter/unreflectSetter 得到的 MethodHandle),后续 get/set 操作不再触发访问控制检查。但以下细节极易出错:
- 基本类型字段(如 int x)对应 VarHandle 的 varType 是 int.class,不是 Integer.class
- 数组字段(如 String[] data)的 varType 必须写成 String[].class,不能简化为 Object[].class
- 调用 invokeExact() 时传参类型必须与字段声明类型完全一致,invoke() 会尝试自动转换,但私有字段句柄不支持这种宽松适配
常见误用与后果
这些操作看似接近目标,实则违反 JVM 规范,运行时直接失败:
- MethodHandles.publicLookup().in(SomeClass.class).findVarHandle(...) → NoSuchFieldException(publicLookup 根本看不到 private)
- 用 findStaticGetter 查找实例字段 → WrongMethodTypeException(签名不匹配)
- 缓存了一个在 ClassA 中创建的 Lookup 实例,却在 ClassB 中用它查 ClassC 的字段 → IllegalAccessException(lookup 权限绑定创建点类)
本质上,Lookup 是访问控制的“执行者”,不是“决策者”。它的设计目标是在句柄构建期把权限问题一次性厘清,既保障安全,又释放调用期性能。










