Java 反射
反射如何在运行时检查与调用成员:能力、成本、封装绕过与框架使用边界。
#type / concept
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
Java 反射
这个概念为什么出现
框架要在不知道具体类的情况下注入依赖、映射 JSON、调用测试方法。反射提供运行时的类型信息与调用能力,但昂贵且能破坏封装。
[!abstract] 一句话理解 反射通过
Class/Method/Field在运行时读取结构并调用;适合框架与工具,不适合业务热路径的默认写法。
最小可运行示例
场景:插件式计算器按方法名调用
import java.lang.reflect.Method;
public class ReflectCall {
public int add(int a, int b) { return a + b; }
public static void main(String[] args) throws Exception {
ReflectCall target = new ReflectCall();
Method m = ReflectCall.class.getMethod("add", int.class, int.class);
Object result = m.invoke(target, 2, 3);
System.out.println(result);
}
}
javac ReflectCall.java && java ReflectCall
期望:
5
结合场景再看三个关注点
- 方法查找按名与参数类型。
- invoke 异常包装为
InvocationTargetException。 - 模块强封装可能阻止 setAccessible。
核心概念与准确模型
1. 入口
obj.getClass() / Class.forName / .class
2. 成员访问
getMethod(public)/ getDeclaredMethod(含非 public)
3. 代价
- 失优化、检查开销
- 安全与封装风险
边界情况与反直觉行为
- 泛型擦除使反射拿不到完整运行时参数类型。
- 基本类型与包装类型在 Class 字面量上不同。
- 原生镜像/关闭反射配置时失败。
常见误区
[!warning] 常见误区:业务代码遍地反射 优先接口多态与生成代码。
工程实践
- 反射集中在框架边界并缓存 Method 句柄。
- 考虑
MethodHandle。 - 明确 opens/exports。
- 安全审核 setAccessible。
本节总结
- 运行时元编程利器
- 成本与封装代价
- 框架常用、业务慎用
自测题
- getMethod 与 getDeclaredMethod 差别?
- 为什么 JSON 库爱反射或代码生成?
参考答案
- 前者看 public 成员(含继承);后者看本类声明成员含非 public。
- 需要在未知类型上读写属性;生成代码可降运行时成本。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Reflection Trail | 教程 | 反射 |
| java.lang.reflect | API | 包 |