反射性能瓶颈主要在获取Constructor/Method实例而非invoke()调用,应缓存反射句柄、预校验参数类型、避免高频使用,并优先用MethodHandle或字节码生成替代。

反射创建对象和调用方法比直接调用慢,但实际影响取决于使用频率和场景;合理缓存、预检查和避免高频反射可让性能落差控制在可接受范围内。
构造器与方法对象应尽量复用
每次调用 Class.getDeclaredConstructor() 或 Class.getMethod() 都会触发查找与安全检查,开销不小。真正耗时的不是后续的 invoke(),而是反复获取反射成员的过程。
- 将
Constructor或Method实例缓存到静态变量或容器中(如ConcurrentHashMap),避免重复解析 - 注意设置
setAccessible(true)应在缓存前完成,否则每次调用仍需权限校验 - 若类结构稳定(如框架处理固定注解类型),可在初始化阶段批量构建并缓存反射句柄
newInstance() 已过时,优先用 Constructor.newInstance()
Class.newInstance() 不仅不支持带参构造,还会绕过访问控制检查逻辑,在 Java 9+ 中已被标记为废弃,且某些模块化环境下直接抛异常。
- 统一使用
Constructor<t> ctor = clazz.getDeclaredConstructor(...); ctor.setAccessible(true); ctor.newInstance(...);</t> - 对无参构造,也建议显式获取再调用,语义清晰且兼容性更好
- 若目标类有多个构造器,注意捕获
NoSuchMethodException并按参数类型精确匹配
方法调用前做必要校验,减少运行时失败
反射调用失败常发生在运行时(如参数类型不匹配、实例为 null、方法不可访问),而这些本可在准备阶段发现。
- 调用前确认目标方法是否为
static:非静态方法必须传入非 null 实例对象,否则抛NullPointerException - 用
Method.getParameterTypes()校验实参类型是否可赋值,避免因自动装箱/泛型擦除导致IllegalArgumentException - 对 public 方法,仍建议调用
setAccessible(true)——JVM 对 public 成员的访问检查并未完全跳过,尤其在安全管理器启用时
高频场景下考虑生成字节码替代反射
当某类方法被反复通过反射调用(如 JSON 反序列化、ORM 字段赋值),每次 invoke() 的动态分派和栈帧开销会累积成瓶颈。
- 使用
MethodHandle(Java 7+)或VarHandle(Java 9+)替代传统反射,它们更接近原生调用,JIT 优化程度更高 - 框架级场景(如 Spring、Jackson)倾向在启动时为常用类生成代理类或 LambdaMetafactory 匿名类,把反射转为直接调用
- 简单字段读写也可用 Unsafe(不推荐)或直接编译 ASM 字节码,但需权衡维护成本与性能收益
反射不是性能毒药,而是需要理解其开销分布的工具。多数业务代码中单次反射调用的影响微乎其微,真正要注意的是别让它出现在 tight loop 里,也别忽略缓存和校验带来的稳定性提升。











