methodhandle 比反射快50%以上,关键在于将权限检查、类型适配等开销前置到句柄创建阶段,调用时仅做轻量类型匹配与跳转;前提必须缓存句柄、用invokeexact、避免astype重复转换,并优先选用varhandle处理字段访问。

MethodHandle 能比反射快,但不是靠“换了个写法”,而是把校验和适配压到创建阶段,调用时只做最轻量的类型匹配和跳转。它快的前提是:句柄复用、类型严格、调用稳定。
缓存句柄,别在热路径里反复查找
每次调用 Lookup.findVirtual() 或 findGetter() 都要走符号解析、权限检查、签名验证,开销远超 Method.invoke()。这一步必须只做一次。
- 用 static final 字段缓存已构建好的句柄,比如:
private static final MethodHandle GET_ID = lookup.findGetter(User.class, "id", int.class); - 动态场景(如 ORM 映射任意类字段)用 ConcurrentHashMap 按 Class + 字段名 + 类型 组合作为 key 缓存
- 不要在 for 循环或高频 setter 中重复 new Lookup 或调用 findXXX
用 invokeExact,别碰 invoke
invokeExact() 要求参数和返回值类型与句柄的 MethodType 完全一致——int.class 就不能传 Integer,String.class 就不能传 Object。不匹配直接抛 WrongMethodTypeException,但换来的是零装箱、零参数数组、JIT 可内联。
- 调试时先打印 handle.type(),和你传的实参类型逐个对齐
- 需要适配?用 asType() 在初始化阶段一次性转换并缓存,别在每次调用前转
- 避免在循环里反复 asType,那会生成新句柄,破坏内联机会
构造句柄时指定精确类型,避开泛型擦除和包装类陷阱
查字段用 findGetter(Target.class, "field", int.class),而不是 findGetter(Object.class, "field", int.class);调方法时 MethodType.methodType(String.class, int.class) 必须和目标方法签名一字不差。
- 基本类型字段必须用 int.class、long.class 等原始类型,用 Integer.class 会触发自动装箱,性能掉回反射水平
- 私有成员要用 MethodHandles.privateLookupIn(Owner.class, lookup) 获取有权限的 Lookup,不能依赖 setAccessible(true)
- JDK 9+ 模块环境下,跨模块访问需确保目标模块 opens 包,否则 findXXX 可能返回 null
字段访问优先考虑 VarHandle(Java 9+)
如果只是读写 public 或普通实例字段,VarHandle 比 MethodHandle 更轻:无对象分配、无异常路径、无 invoke 开销,实测快 10–20%。
- 获取方式:
VarHandle vh = MethodHandles.lookup().findVarHandle(Owner.class, "field", int.class); - 调用方式:
int val = (int) vh.get(instance);
vh.set(instance, 42); - 它不支持方法调用,但对字段操作就是 JVM 层面的“第一类支持”
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











