Java反射核心是运行时动态操作类,始于Class对象,经Constructor/Method/Field三步完成实例化、调用与字段访问,支撑框架解耦、自动映射等场景,但需权衡性能与安全。

学Java反射机制,关键不是背API,而是理解“运行时动态操作类”这个核心逻辑。它不像普通编码那样编译期就定死,而是在程序跑起来之后才去探知、加载、调用——这种“后发制人”的能力,才是Spring、MyBatis这些框架能灵活装配Bean、自动映射数据库结果的底层原因。
从Class对象开始,搞懂反射的唯一入口
所有反射操作都始于Class对象,它是JVM为每个类在内存中生成的唯一元数据镜像。不是实例对象,而是“类本身”的运行时表示。比如User.class、obj.getClass()或Class.forName("com.example.User")拿到的,都是同一个Class对象。它不随new多少次User而变,只有一份。
- 类名.class:编译期已知类,最安全,不触发类初始化
- 对象.getClass():已有实例时快速获取,适合类型不确定的泛型场景
- Class.forName():真正“动态”的方式,根据字符串加载类,框架配置驱动的核心
按三步走,掌握反射的标准操作流
反射不是零散调用,而是一个清晰链条:先拿到Class → 再找构造器/方法/字段 → 最后执行动作。每一步都对应一个核心类:
-
Constructor:用
getDeclaredConstructor()获取私有构造器,配合newInstance()创建对象(注意JDK9+推荐newInstantiator()替代) -
Method:用
getMethod()(公有)或getDeclaredMethod()(含私有),再调setAccessible(true)突破访问限制,最后invoke()执行 -
Field:类似Method,
getDeclaredField()+setAccessible(true)+get()/set()即可读写任意字段,包括private和static
结合真实场景,理解为什么非用不可
光会写clazz.getMethod("xxx").invoke(obj)没意义,得知道它解决什么实际问题:
立即学习“Java免费学习笔记(深入)”;
-
框架解耦:Spring通过
Class.forName()加载配置里写的类名,再反射创建Bean,业务代码完全不用new具体实现类 -
结果集自动映射:MyBatis查出一行数据库记录,用反射遍历实体类所有
setter方法,把列名匹配字段名后自动赋值,省掉手动user.setName(rs.getString("name")) -
通用工具封装:写一个
copyProperties(src, dest)工具,靠反射读取src所有getter、调用dest所有setter,一套代码适配任意两个同类结构对象 - 插件热加载:系统运行中从jar包加载新类,用反射实例化并注册为服务,无需重启应用
别跳过性能和安全这两道坎
反射强大但有代价。每次getMethod()都要遍历方法列表,setAccessible(true)会关闭JVM安全检查,频繁调用比直接调用慢5–10倍。实际项目中要:
- 缓存
Method或Constructor对象,避免重复查找 - 只在必要时设
setAccessible(true),尤其生产环境需评估安全风险 - 非核心逻辑(如日志、监控)可用反射;高频路径(如交易核心)优先用编译期确定的方式


















