findVirtual用于实例方法,首参必须是接收者对象,支持多态分派;findStatic用于静态方法,无接收者参数,方法归属由声明类静态确定,调用触发类初始化。

Lookup对象获取方法句柄时,findVirtual和findStatic的查找逻辑本质不同,核心在于调用目标是否依赖接收者(receiver)以及JVM指令语义的映射关系。
调用目标与接收者绑定方式不同
findVirtual用于查找**实例方法**,要求提供明确的声明类(如 String.class)、方法名(如 "substring")和完整签名(MethodType)。它生成的方法句柄在调用时,**第一个参数必须是目标对象实例**(即 receiver),后续才是原方法的参数。这对应字节码中的 invokevirtual 指令行为,支持多态分派。
- 例如:
lookup.findVirtual(String.class, "length", MethodType.methodType(int.class))返回的句柄,调用时需传入一个String实例作为首参 - 即使目标类是 final 的,或方法被子类重写,findVirtual 仍按虚拟调用规则解析——它不锁定具体实现类,而是依据运行时实际类型选择方法
方法归属与类初始化时机不同
findStatic 用于查找**静态方法**,同样需要声明类、方法名和签名,但生成的句柄**不接受 receiver 参数**,调用时直接传入原方法定义的全部参数。它对应 invokestatic 指令,方法归属由声明类静态确定,不涉及动态分派。
- 例如:
lookup.findStatic(Math.class, "max", MethodType.methodType(int.class, int.class, int.class))返回的句柄,调用时只需两个int参数 - 调用该句柄会触发
Math.class的初始化(如果尚未初始化),这是 JVM 规范强制要求,而 findVirtual 不保证触发目标类初始化(仅当 receiver 实例化或首次访问时才可能触发)
权限检查与符号解析路径不同
两者都执行严格的访问控制检查,但检查依据不同:
- findVirtual 要求 Lookup 对象所在类对目标方法具有访问权限(如包内可见、public 或 protected),且目标方法不能是 private 或 static;它还会验证方法是否可被虚拟调用(即非 final、非 private、非 static)
- findStatic 同样校验访问权限,但允许目标为 public/static 方法,且不要求声明类与 Lookup 所在类存在继承关系;若方法被标记为
ACC_VARARGS(0x0080),返回句柄自动具备asVarargsCollector行为 - 二者在查找阶段都会进行符号解析(symbol resolution),但 findVirtual 需额外完成虚方法表(vtable)或接口方法表(itable)的索引定位,开销略高于 findStatic
使用场景与性能提示
不要在循环中反复调用 findVirtual 或 findStatic —— 它们是重操作,涉及签名匹配、权限验证和字节码层面的链接准备。应预先缓存 MethodHandle 实例。
- 适合 findVirtual 的场景:需动态调用对象实例方法,且希望保留多态性(如通用对象属性提取器)
- 适合 findStatic 的场景:调用工具类静态方法(如
Objects.equals、String.valueOf),或构建无状态函数式管道 - 注意:findVirtual 不能用于 private 方法(要用 findSpecial),也不能用于 interface default 方法(需用 findVirtual + 接口类型,但实际行为取决于实现类)

















