Java 反射机制是什么?运行时如何创建对象和调用方法?
下面是一段教学用的模拟面试。
🧑💻 面试官:Java 反射是什么?
🙋♂️ 我:运行时拿到类的信息,可以创建对象、访问字段和调用方法。
🧑💻 面试官:那拿到一个 Class,就能直接调用里面所有方法吗?private 方法也随便调用?
🙋♂️ 我:还要找到具体成员,并满足访问条件。
🧑💻 面试官:同名方法有两个重载,怎么选?目标方法自己抛异常时,和你根本没有调用权限,得到的是同一种错误吗?
反射把“编译时写死的调用”,变成运行时查找和执行;查找、权限、目标方法失败,仍然是三件事。
面试速答(60 秒版)
Java 反射让代码在运行时,通过 Class 等对象检查类型与成员,并使用 Constructor、Method、Field 等 API 创建实例、调用方法或访问字段。
普通调用在编译时就写明类型和方法;反射则可以根据运行时信息选择,因此常用于框架装配、注解处理和动态适配。但方法名称只是查找条件之一,还要提供参数类型,调用时也要有正确对象和参数。
反射并不等于绕过全部访问限制。访问检查和 Java 模块的开放规则仍然存在;目标方法抛出的异常也需要保留。实际使用时,应限制可反射的类型与成员,避免把外部输入直接变成任意类名和方法名。

知识点详解:配置写了一个类名,框架怎么让它工作?
为什么普通调用有时不够用?
假设咱们写一个通知系统。项目里有邮件发送器和短信发送器,配置决定本次使用哪一个。
最简单的实现,可以用明确的注册表,把配置里的名字映射到已经认可的工厂函数。这不一定需要反射。
但有些框架希望统一扫描类型,读取注解,检查构造器,然后创建符合规则的组件。框架编写时未必知道用户将提供哪些具体类,就需要在运行时检查这些类型。
反射解决的是这类“运行时检查和操作类型”的需求,不是看到配置就必须使用反射。先说明动态性从哪里来,才能说明它为什么有价值。
Class 是类型入口,不是业务实例
咱们可以从已有对象取得它的 Class,也可以通过类型字面量等方式取得;根据类名查找类型是另一种入口,涉及加载与初始化的具体规则。
拿到 Class 以后,能查询这个类型的名称、接口、构造器、字段和方法等信息。它描述的是类型,不是自动创建出来的邮件发送器实例。
因此,拿到 Class 只是第一步。要创建实例,还需要找到合适的 Constructor,并提供参数调用它。反射包 API分别定义了这些对象的职责。
不能把“查询类信息”“创建对象”“初始化类”全部压成一句“反射执行类”。这些步骤发生了什么,会影响异常与副作用。
找到方法以后,调用还缺哪些条件?
假设发送器有两个 send 方法:一个接收字符串,另一个接收消息对象。
只知道名字叫 send,不够确定目标。查找时还要说明参数类型。getMethod 查询指定签名的公开方法,也包含符合规则的继承成员;getDeclaredMethod 则查询该类自己声明的方法,不自动把父类所有方法都加进来。
找到的 Method 像一份方法描述。调用实例方法时,还要提供正确的目标对象以及实际参数;静态方法则不依赖某个业务实例。
整个过程可以这样理解:
得到 Class
→ 按名称与参数类型找到 Method
→ 准备正确的目标对象和参数
→ 通过访问检查
→ 执行目标方法,取得结果或异常
这里用流程说明 Java 的特定 API,不提供机械翻译成 TypeScript 或 Python 的“同名实现”。其他语言的动态调用方式并不等同于 Java 反射体系。

为什么 setAccessible 不是万能钥匙?
访问 private 成员时,普通访问检查可能阻止调用。某些条件下,可以请求抑制语言级访问检查。
但现代 Java 还有模块边界。跨模块访问一个未向调用方开放的包,不能仅靠 setAccessible(true) 保证成功。trySetAccessible 在不能启用访问时返回 false,而 setAccessible 可能抛出 InaccessibleObjectException。AccessibleObject 文档写明了限制。
同样,也不能推导成“所有 final 字段都可以随便改”。某些 final 字段不能因此取得写权限。
所以,框架要使用深层反射,应有清楚的模块与访问约定。遇到失败就给 JVM 添加宽泛开放参数,虽然可能让眼前代码跑起来,却不等于这个依赖方式合理。

失败在调用前,还是发生在方法里面?
假设 Method 已经找到,但调用方没有权限,可能得到 IllegalAccessException。参数数量或类型不匹配,则可能得到 IllegalArgumentException。
这两种情况说明调用过程本身没有按约定完成,不能算目标发送器正常执行后报告了业务失败。
如果目标方法真正开始执行,并在里面抛出异常,Method.invoke 会用 InvocationTargetException 包装它。排查时需要继续看 cause,才能看到发送器里面真正发生的错误。Method.invoke 约定区分了这些情况。
日志如果只保留最外层包装,就很容易把网络超时写成笼统的“反射失败”。但展开原因,也不意味着随便丢掉调用上下文。

动态调用,边界要比字符串更清楚
假设接口允许用户传入类名与方法名,后端照单查找并执行。这不是一个普通的可配置功能,可能把整个应用暴露成任意调用入口。
实际应通过允许列表、接口约束、参数校验和权限检查,把动态范围收在明确能力内。能在编译时直调的普通业务,也没必要全部改成反射。
性能上先关注查找次数与真实热点。可以缓存稳定的成员描述,但缓存对象还可能影响类加载器的存活。既不要背一个跨版本的“慢几倍”,也不要把缓存当成没有生命周期的全局表。
面试官继续追问
反射和动态代理是同一件事吗?
不是。反射负责运行时检查和操作类型;代理负责把调用交给另一层处理。它们可能在框架里配合使用,但不是互相替代的概念。
getDeclaredMethod 能拿到所有父类方法吗?
不能。它的范围是当前类声明的相应方法。需要继承范围时,按具体访问要求选择其他查询方式或遍历类型层级。
有注解,就一定能在运行时读到吗?
不一定。注解的保留策略等规则决定运行时是否可见。不能把源码里看得见的注解,全部当成反射一定能读到的数据。
面试速记卡
- Class:类型信息入口,不等于已经创建业务实例。
- 成员:Constructor 创建对象,Method 调用方法,Field 访问字段。
- 查找:名称与参数类型一起决定方法,声明范围和继承范围分开看。
- 权限:反射仍受访问与模块开放规则约束。
- 失败:区分调用前失败与目标异常,保存真正的 cause。
公司面试真题
真题根据求职者公开面经整理,题意经过概括,非逐字原话或公司官方题库;本文为 Sunday 的独立解析。
京东 · Java后台 · 校招
Java 反射的原理和应用场景是什么?(题意整理)
京东 Java 后台三面凉经 ↗
原帖编辑于 2019-08-23(历史校招面经)