反射是什么?:
在日常工作中是用反射的机会不多,使用反射主要是当需要动态操作类的时候以及在框架中使用例如Spring中的依赖注入,JUnit的测试方法调用。
大白话介绍就是:Java 中的 反射(Reflection) 就像给程序一面「照妖镜」,让它能在运行时动态地「看到」自己的结构(比如类、方法、字段等),甚至能调用原本无法直接访问的代码。这种能力让程序变得更灵活,但也会带来性能开销和安全风险。
反射原理:
Java 在编译时会将类的信息(类名、方法、字段、注解等)保存在 .class 文件中。当类被加载到内存时,JVM 会为每个类生成一个唯一的 Class 对象,这个对象就像一个「身份证」,存储了类的所有元数据。反射的本质就是通过 Class 对象去读取和操作这些元数据。
(理解需要一点JVM知识)
能做什么?:
- 运行时动态获取类的信息
- 比如获取类的名称、父类、接口、方法、字段等。
- 动态创建对象
- 即使不知道类名,也能通过字符串构造对象。
- 调用私有方法或访问私有字段
- 突破访问权限限制(需谨慎使用)。
- 实现通用框架
- 比如 Spring 通过反射创建 Bean、JUnit 通过反射调用测试方法。
基本使用:
简化版
第一步:获取class对象
// 1. 通过类名获取
Class<?> clazz = String.class;
// 2. 通过对象获取
String str = "Hello";
Class<?> clazz = str.getClass();
// 3. 通过全类名动态加载(最常用)
Class<?> clazz = Class.forName("java.lang.String");
第二步:操作类
// 创建对象(调用无参构造)
Object obj = clazz.getDeclaredConstructor().newInstance();
// 获取方法并调用
Method method = clazz.getMethod("length");
int length = (int) method.invoke(obj); // 相当于调用 obj.length()
// 访问私有字段(需要解除安全检查)
Field field = clazz.getDeclaredField("value");
field.setAccessible(true); // 突破 private 限制
byte[] value = (byte[]) field.get(obj);
反射的原理细节
- Class 对象是反射的入口
- JVM 在类加载阶段(ClassLoader.loadClass())解析 .class 文件,生成 Class 对象。
- 元数据存储在方法区
- 类的方法、字段等信息存储在 JVM 的方法区(Method Area),反射通过 Class 对象访问这些数据。
- 反射调用方法的本质
- 调用 method.invoke() 时,JVM 底层通过 MethodAccessor 实现,首次调用会生成一个 NativeMethodAccessorImpl(基于本地方法),后续可能生成动态字节码加速(GeneratedMethodAccessor1)。
实际使用场景
- Spring 框架:通过 @Autowired 动态注入对象。
- JUnit:通过反射识别并执行 @Test 方法。
- JSON 序列化工具:将对象的字段名和值动态转换为 JSON 字符串。
总结
反射是 Java 的「自省」能力,通过 Class 对象在运行时探索和操作类的结构。它让代码更灵活,但需谨慎使用——能不用则不用,除非你正在写框架或处理动态逻辑。
2820

被折叠的 条评论
为什么被折叠?



