methodhandles.lookup 是 jvm 动态生成的访问令牌,由 methodhandles.lookup() 返回,不可 new 实例,构造器私有且绑定调用者类权限;跨类传递、缓存或误用 findxxx 会导致 illegalaccessexception 或查找失败。

MethodHandles.Lookup 是什么,为什么不能直接 new
它不是普通类,而是 JVM 在每次获取反射入口时动态生成的“访问令牌”,由 MethodHandles.lookup() 静态方法返回。你无法用 new MethodHandles.Lookup() 实例化——JVM 会直接抛 IllegalAccessException。这是因为它的构造器是私有的,且只允许在调用者类的上下文中创建,目的是绑定访问权限到当前类的运行时身份。
常见错误现象:java.lang.IllegalAccessException: no such method: Class.methodName 或 Lookup has private access,往往是因为试图跨类传递或缓存 Lookup 实例,导致权限上下文丢失。
- 只能在目标类内部调用
MethodHandles.lookup()获取自己的Lookup实例 - 不能把
Lookup存成静态字段或传给其他类使用——它不跨类生效 - 子类调用
super.lookup()不行,Lookup不继承,必须在子类里重新调用MethodHandles.lookup()
lookup().findVirtual() 找不到 public 方法?检查方法签名和接收者类型
findVirtual() 要求第一个参数是“接收者类型”(即方法定义所在的类),第二个是方法名,第三个是 MethodType 描述签名。很多人栽在类型擦除或泛型上:比如对 List<string></string> 调用 get(int),MethodType.methodType(Object.class, int.class) 才对,不能写 String.class —— 运行时泛型已擦除,JVM 只认桥接后的真实返回类型。
使用场景:想绕过接口/父类限制,直接调用某个具体实现类的 public 实例方法,但又不想用传统 Class.getDeclaredMethod() + setAccessible(true)。
立即学习“Java免费学习笔记(深入)”;
- 接收者类型必须是方法实际声明的类(不是调用方类,也不是接口),例如
ArrayList而非List - 方法名和参数类型必须完全匹配,连
int和Integer都不能混用 - 返回类型在
MethodType中写的是编译期擦除后的类型,如Collection.size()返回int,不是Integer
想访问 private 方法?必须在定义该方法的类里调用 lookup().findSpecial() 或 findPrivate()
findSpecial() 和 findPrivate() 都要求调用发生在方法所属类的内部。区别在于:findSpecial() 用于调用本类或父类的非 private 实例方法(类似 super. 调用),而 findPrivate() 专用于本类的 private 方法——它甚至不支持访问父类 private 方法。
容易踩的坑:有人在工具类里写个通用反射方法,传入 Class 和 String methodName,然后调用 lookup().findPrivate(targetClass, ...),这必然失败。因为 Lookup 权限只认“当前类”,不认你传进来的 targetClass。
-
findPrivate()的第一个参数必须是当前类字面量,比如MyUtils.class,且该方法必须定义在MyUtils里 -
findSpecial()可用于调用父类的 protected 方法,但接收者类型仍得是当前类或其子类(不能是任意类) - 没有
findProtected(),protected 方法走findVirtual()即可(只要在同一个包或子类中)
MethodHandle 性能比反射快,但首次解析开销大,别在热路径反复 lookup
MethodHandle 执行时确实接近直接调用,比 Method.invoke() 快 2–5 倍(视 JDK 版本),但它背后依赖 JVM 的内联与优化。问题出在“解析阶段”:每次调用 findXXX() 都触发一次符号解析、权限校验和句柄生成,这部分开销远高于缓存一个 Method 对象。
性能影响:在循环里反复 lookup().findVirtual(...) 是典型反模式;而把 MethodHandle 缓存为 static final 字段,则能发挥最大优势。
- 只在类初始化或首次使用时解析一次,存成
static final MethodHandle - 不要把
Lookup实例缓存起来复用——它本身不重,但复用它查不同类的方法会失败 - JDK 9+ 加入了
MethodHandles.privateLookupIn(Class, Lookup),可用于跨类安全获取 private 访问权,但它仍要求调用方有RuntimePermission("accessDeclaredMembers")
真正难的不是语法,是理解 Lookup 的权限边界如何随字节码位置实时变化——它不像 SecurityManager 那样可配置,而是硬编码在 JVM 校验逻辑里,改错一行调用位置,就可能从成功变失败。









