JJava 知识库
JAVA INTERVIEW

高频面试题

Java基础进阶约 3 分钟

你们项目里哪些地方用到了反射和注解?如果启动变慢或者反射调用报错,你会怎么处理?

参考回答约 3 分钟 · 口语表达
先说结论

我会先控制影响,再按证据定位:反射让程序在运行期读取类型信息并操作构造器、字段和方法;注解负责声明元数据,只有 RUNTIME 注解才能通过运行时反射读取。Spring 的依赖注入、ORM 映射和测试框架都大量使用二者。 获取 Class 可以使用类字面量、实例的 getClass() 或 Class.forName();

01

我会先确认影响范围,同时控制故障继续放大。理解 Class 元数据、反射调用、注解保留策略及框架中的典型应用。反射让程序在运行期读取类型信息并操作构造器、字段和方法;注解负责声明元数据,只有 RUNTIME 注解才能通过运行时反射读取。Spring 的依赖注入、ORM 映射和测试框架都大量使用二者。 获取 Class 可以使用类字面量、实例的 getClass() 或 Class.forName();后者还可能触发类初始化,不能把三种方式完全等同。

02

止损之后,我会按请求链路建立证据,而不是凭经验猜。沿着「加载类元数据 → 读取注解配置 → 查找成员 → 访问或调用目标 → 缓存元数据并治理边界」观察输入、状态与输出,这些阶段都可以从日志、指标或源码里验证。 加载类元数据 Class 对象连接字节码元数据与运行期类型,类加载时机和类加载器决定同名类型是否真正相同。 读取注解配置 只有保留策略为 RUNTIME 的注解才能在运行期读取,Target 决定它允许出现在哪类程序元素上。 java.lang.Class#getDeclaredMethods:声明成员扫描和访问边界。 java.lang.invoke.MethodHandles:Lookup 与 MethodHandle 的类型安全调用。

03

定位时我最关注这些参数、指标和容量关系。用 JMH 对比每次 getDeclaredMethods、缓存 Method 和 MethodHandle;分别记录冷启动扫描与稳态调用,不能混为一个平均值。

04

找到根因后先做最小修复,再用同样的流量验证。对象映射框架在每次请求中扫描字段、读取注解并执行 setAccessible,首请求与高并发下都产生明显开销。把扫描移动到启动阶段、缓存不可变元数据并为失败映射提前校验后,延迟和线上不确定性都显著下降。 每次请求重复扫描元数据:冷启动扫描时长:把扫描移到启动期并用 ClassValue 缓存。 固定输入和基线 先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「冷启动扫描」为主基线,记录值应满足「应用预算内一次完成」;同时保存 冷启动扫描时长、反射调用热点,使后续变化能够回到同一时间轴比较。

05

恢复阶段要逐步放量,最后把监控和边界补齐。方案:更适合的场景:主要收益:代价与边界。 直接反射:低频工具、插件发现和框架启动期:灵活且无需生成代码:运行期错误和访问成本较高。 MethodHandle:高频动态调用且签名可管理:JIT 优化空间更好:API 与类型适配更复杂。 编译期代码生成:映射关系稳定、性能敏感:运行时快速且错误前置:构建链更复杂,动态性较弱。 选型至少带上 对象创建率、调用频次、输入规模和 API 边界,并用上面的量化基线验证;

排查与恢复时间线从目标到落地
01加载类元数据
02读取注解配置
03查找成员
04访问或调用目标
05缓存元数据并治理边界