JAVA的反射机制
Homework2 to Blog:了解Java的反射机制
前言
反射是Java语言中一个非常强大的特性,它允许程序在运行时动态地获取类的信息、调用方法、访问字段,甚至改变对象的访问权限。理解反射对于框架开发、动态代理、注解处理等场景至关重要。
1. 什么是反射(Reflection)?
反射 是指程序在运行状态中,可以动态获取任意类的完整结构(包括类名、方法、字段、构造器、注解等),并能够动态创建对象、调用方法、修改字段值的一种能力。Java 的反射 API 主要位于 java.lang.reflect 包中。
简单来说,反射让 Java 变得“动态”——虽然 Java 是静态编译型语言,但通过反射,我们可以在运行时“窥探”和“操纵”类。
2. 反射的核心类
| 类名 | 作用 |
|---|---|
Class |
代表一个类或接口的运行时实例,是反射的入口 |
Field |
代表类的成员变量(字段) |
Method |
代表类的方法 |
Constructor |
代表类的构造器 |
Modifier |
用于解析访问修饰符(public、private 等) |
Array |
用于动态创建和操作数组 |
3. 获取 Class 对象的三种方式
// 方式1:通过类名.class
Class<?> clazz1 = String.class;
// 方式2:通过对象的 getClass() 方法
String str = "hello";
Class<?> clazz2 = str.getClass();
// 方式3:通过 Class.forName()(全限定类名,最常用)
Class<?> clazz3 = Class.forName("java.lang.String");
4. 通过反射创建对象
// 使用无参构造器
Class<?> clazz = Class.forName("java.util.ArrayList");
Object obj = clazz.newInstance(); // 已过时,但仍可用
// 推荐使用 Constructor
Constructor<?> constructor = clazz.getConstructor();
Object list = constructor.newInstance();
5. 通过反射获取和调用方法
Class<?> clazz = Class.forName("java.lang.String");
Object str = clazz.getConstructor(String.class).newInstance("Hello");
// 获取 public 方法 length()
Method method = clazz.getMethod("length");
int len = (int) method.invoke(str); // 输出 5
// 获取 private 方法(需要 setAccessible)
Method privateMethod = clazz.getDeclaredMethod("checkBounds", byte[].class, int.class, int.class);
privateMethod.setAccessible(true); // 打破封装
privateMethod.invoke(str, new byte[10], 0, 5);
6. 通过反射访问字段
Class<?> clazz = Class.forName("java.lang.Integer");
Field field = clazz.getDeclaredField("value"); // Integer 内部持有 int value
field.setAccessible(true);
Integer num = 100;
field.set(num, 200); // 修改了 Integer 对象内部的值
System.out.println(num); // 输出 200(但不推荐这样做)
7. 反射的应用场景
- 框架开发:Spring、Hibernate、MyBatis 等大量使用反射来实现 IoC、AOP、ORM。
- 动态代理:JDK 动态代理基于反射生成代理类。
- 注解处理:运行时通过反射读取注解信息(如
@Override、@Autowired)。 - IDE 智能提示:开发工具利用反射分析类结构。
- 调试和测试工具:动态获取对象状态。
8. 反射的优缺点
| 优点 | 缺点 |
|---|---|
| 极大提高程序的灵活性和扩展性 | 性能开销较大(比直接调用慢) |
| 支持运行时动态决策 | 破坏封装性,可能带来安全隐患 |
| 是实现通用框架的基础 | 代码可读性下降,调试难度增加 |
9. 性能优化建议
- 如果频繁调用同一个方法,可以将
Method对象缓存起来,避免重复查找。 - 使用
setAccessible(true)时注意权限管理。 - 尽量将反射操作放在初始化阶段,而不是每次请求都执行。
10. 简单示例:通过反射打印类的全部信息
public class ReflectionDemo {
public static void printClassInfo(String className) throws Exception {
Class<?> clazz = Class.forName(className);
System.out.println("类名:" + clazz.getName());
System.out.println("简单类名:" + clazz.getSimpleName());
System.out.println("修饰符:" + Modifier.toString(clazz.getModifiers()));
System.out.println("\n--- 字段 ---");
for (Field f : clazz.getDeclaredFields()) {
System.out.println(Modifier.toString(f.getModifiers()) + " " +
f.getType().getSimpleName() + " " + f.getName());
}
System.out.println("\n--- 方法 ---");
for (Method m : clazz.getDeclaredMethods()) {
System.out.println(Modifier.toString(m.getModifiers()) + " " +
m.getReturnType().getSimpleName() + " " +
m.getName() + "()");
}
System.out.println("\n--- 构造器 ---");
for (Constructor<?> c : clazz.getDeclaredConstructors()) {
System.out.println(Modifier.toString(c.getModifiers()) + " " +
c.getName());
}
}
public static void main(String[] args) throws Exception {
printClassInfo("java.util.ArrayList");
}
}
总结
反射是 Java 高级开发必须掌握的核心技能。它让我们能够写出更通用、更灵活的代码,但同时也伴随着性能和安全的权衡。在实际项目中,要合理使用反射,避免滥用。通过本次作业的学习,相信你对反射有了更深入的理解。
下一步:可以继续学习动态代理、注解处理器以及 Spring 框架中反射的典型应用。
本文是 Homework2 的博客化输出,旨在巩固反射知识并分享给更多同学。如有问题,欢迎在评论区交流讨论。

浙公网安备 33010602011771号