反射是让Java程序在运行时能动态感知并操作类结构的能力,核心是Class对象,本质是弥补编译期类型信息固定带来的局限;它通过延迟绑定支持框架实现插件化、热加载等能力,但存在性能损耗与封装性削弱问题。

面试时讲反射,别堆术语,重点说清它“为什么存在”和“解决什么问题”。一句话概括:反射是让Java程序在运行时能动态感知并操作类结构的能力,核心是Class对象,本质是弥补编译期类型信息固定带来的局限。
反射存在的根本原因:突破编译期约束
Java是静态语言,正常编码时所有类、方法、字段必须在编译期就确定。但框架(如Spring、MyBatis)不可能提前知道用户写的UserService或OrderDao。反射把类型检查和行为绑定推迟到运行时——程序启动后,再根据配置、注解或字符串加载类、创建实例、调用方法。这就像从“按菜单点菜”升级为“进厨房现场配菜”,支撑了插件化、热加载、通用序列化等能力。
反射的唯一入口:Class对象怎么来
每个类被JVM加载后,都会生成且仅生成一个Class对象,它是该类的元数据镜像,包含类名、父类、接口、字段、方法、构造器、注解等全部结构。获取方式有三种,适用场景不同:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 类名.class:最安全,编译期校验,无异常,适合已知类且无需动态加载
- 对象.getClass():适用于已有实例,常用于泛型擦除后还原实际类型
- Class.forName("全限定名"):最灵活,支持配置驱动、注解解析、网络传参等场景,但会触发类初始化,需捕获ClassNotFoundException
反射能干的三件事:实例化、调用、访问
所有操作都基于Class对象展开:
立即学习“Java免费学习笔记(深入)”;
- 动态实例化:通过getDeclaredConstructor()获取构造器,setAccessible(true)绕过private限制,再用newInstance()创建对象(注意:Class.newInstance()已废弃)
- 动态方法调用:用getDeclaredMethod()定位方法,setAccessible(true)访问私有方法,invoke()执行;参数类型必须精确匹配(int.class ≠ Integer.class)
- 动态字段操作:getDeclaredField()获取字段,setAccessible(true)后用get()/set()读写值;修改final字段可能无效——若编译器已内联(如static final String s = "abc"),运行时改了也看不到效果
回答要带工程分寸:不能只夸灵活
面试官想听你有没有真实落地经验。可以自然补充:
- 性能损耗明显:每次invoke或set都有安全检查、类型转换、栈帧开销,比直接调用慢5–10倍
- 封装性被削弱:可读私有字段、调用private方法,虽方便框架实现,但在Android或安全沙箱中常被禁用
- API易错:getMethod()只查public继承链,getDeclaredMethod()才查本类所有方法;setAccessible(true)漏写、参数类型写错、异常没处理,都会直接抛Exception

















