写Java代码时,很多人听到“反射”两个字就会皱眉,觉得这玩意儿肯定拖慢程序。尤其在一些对性能敏感的场景里,比如高频交易系统或者实时数据处理,大家能不用反射就不用。但实际情况真有这么夸张吗?
反射到底干了啥
简单说,反射就是在运行时动态获取类的信息,比如调用某个方法、访问某个字段,哪怕编译时根本不知道这个类长什么样。常见的使用场景包括Spring的依赖注入、各种ORM框架(像MyBatis)自动映射数据库字段到对象属性。
比如你写这样一段代码:
Class<?> clazz = Class.forName("com.example.User");
Object obj = clazz.newInstance();
clazz.getMethod("setName", String.class).invoke(obj, "张三");
这段代码没有直接 new User(),而是通过字符串找到类,再创建实例、调用方法。灵活性是上去了,但代价呢?
性能瓶颈在哪
反射慢,主要是因为绕过了正常的编译期检查和JVM优化路径。正常方法调用会被JIT编译成高效的本地代码,而反射调用需要经过更多的中间步骤:查找方法、校验权限、包装参数、触发安全检查……每一步都有开销。
举个例子,直接调用 obj.setName("张三") 可能只要1纳秒,而用反射 invoke 一次可能要几十甚至上百纳秒。差距看起来大,但得看调用频率。如果你一天只调这么一次,那总共才多花0.1毫秒,用户根本感觉不到。
缓存可以救场
很多人一上来就否定反射,其实是没考虑优化手段。像 Method、Field 这些反射对象是可以缓存起来重复用的。第一次查一遍,后面直接 invoke,省掉查找过程,速度能提升一大截。
public class UserReflectionHelper {
private static final Map<String, Method> METHOD_CACHE = new HashMap<>();
public static void setName(Object obj, String name) throws Exception {
Method method = METHOD_CACHE.computeIfAbsent(
"setName",
k -> obj.getClass().getMethod(k, String.class)
);
method.invoke(obj, name);
}
}
这种做法在框架里很常见。Spring 就大量使用缓存来降低反射带来的性能损耗。
别妖魔化反射
技术没有原罪,关键是怎么用。你在写一个小型工具脚本,或者配置驱动的业务逻辑,完全没必要为了省那几纳秒去牺牲可维护性。但如果是在一个每秒处理十万订单的系统里,频繁通过反射调用核心方法,那就得掂量掂量了。
现代JVM也在不断优化反射性能,比如从JDK 7开始引入的 MethodHandle,还有反射调用的内联尝试,都让差距在缩小。
真正影响性能的往往不是某一行反射代码,而是滥用。就像炸药能修路也能伤人,看你怎么用。