反射(Reflection)是Java语言中一个非常重要且强大的机制,它赋予了程序在运行时动态获取类的信息、访问对象属性和方法、创建实例等能力。反射机制是Java灵活性和高度扩展性的核心基础之一,广泛应用于Java框架、容器、动态代理等系统设计和开发中。
本文将从反射的概念、应用场景、优缺点入手,详细介绍反射API的使用方法,并结合实例代码进行说明。
一、什么是反射?
Java反射机制,是指在程序运行期间,能够动态获取类的属性、方法、构造函数等信息,并能够操作对象属性、调用对象方法甚至创建对象实例的能力。
换句话说,反射是一种让Java程序有"自省"、"自编程"、"自适应"能力的特殊机制。通过反射,程序可以在不知道对象具体类型的情况下,对对象进行操作。
二、反射的常见用途
- 动态实例化对象
- 获取和操作对象属性(即使是 private)
- 动态调用方法
- 编写通用框架(如Spring、MyBatis等)
- 依赖注入与动态代理
- 开发序列化/反序列化工具
三、反射API的核心类
- Class:代表一个类或接口的字节码对象
- Field:代表类的成员变量(字段)
- Method:代表类的方法
- Constructor:代表类的构造方法
- Array/Modifier/Proxy/Annotation等:辅助类
四、如何获取Class对象
方式有三种:
java
// 1. 类名.class
Class<MyClass> clazz1 = MyClass.class;
// 2. 对象实例.getClass()
MyClass obj = new MyClass();
Class<? extends MyClass> clazz2 = obj.getClass();
// 3. Class.forName("全类名")
Class<?> clazz3 = Class.forName("com.example.MyClass");
五、反射的详细实例代码
下面通过一个实际案例详细演示反射的主要用法。
java
// 假设有如下User类
public class User {
private String name;
private int age;
public User() {} //无参构造器
public User(String name, int age) {
this.name = name;
this.age = age;
}
private void sayHello(String prefix) {
System.out.println(prefix + ", my name is " + name + ", age: " + age);
}
}
1. 动态创建对象实例
java
Class<?> userClass = Class.forName("User");
// 无参构造
Object userObj1 = userClass.newInstance();
// 有参构造
Constructor<?> constructor = userClass.getConstructor(String.class, int.class);
Object userObj2 = constructor.newInstance("张三", 20);
2. 获取并设置属性(包括private属性)
java
// 设置 private 字段
Field nameField = userClass.getDeclaredField("name");
nameField.setAccessible(true); // 暴力反射,允许访问私有字段
nameField.set(userObj1, "李四"); // userObj1.name = "李四"
Field ageField = userClass.getDeclaredField("age");
ageField.setAccessible(true);
ageField.setInt(userObj1, 18);
// 获取字段值
String nameVal = (String) nameField.get(userObj1);
int ageVal = ageField.getInt(userObj1);
System.out.println("name=" + nameVal + ", age=" + ageVal);
3. 反射调用方法(包括private方法)
java
// 调用 private 方法
Method method = userClass.getDeclaredMethod("sayHello", String.class);
method.setAccessible(true); // 允许调用private方法
method.invoke(userObj1, "Hi"); // 输出: Hi, my name is 李四, age: 18
4. 动态获取类的全部信息
java
// 获取所有字段
Field[] fields = userClass.getDeclaredFields();
for (Field f : fields) {
System.out.println("字段:" + f.getName());
}
// 获取所有方法
Method[] methods = userClass.getDeclaredMethods();
for (Method m : methods) {
System.out.println("方法:" + m.getName());
}
// 获取构造器
Constructor[] constructors = userClass.getDeclaredConstructors();
for (Constructor c : constructors) {
System.out.println("构造方法:" + c);
}
六、常见应用场景案例
1. Bean属性注入工具
很多框架用反射为对象注入值:
java
public void setProperty(Object bean, String propName, Object value) throws Exception {
Field field = bean.getClass().getDeclaredField(propName);
field.setAccessible(true);
field.set(bean, value);
}
2. 动态调用
比如写一个通用服务,通过字符串配置类名和方法名,就能调用目标业务代码,而无需写死类型。
七、反射的优缺点
优点
- 极高灵活性,强大扩展力
- 适用于通用性、框架、插件式编程
- 能突破访问权限,做特殊场景下的操作
缺点
- 性能较低:反射速度慢,常规10-20倍于直接调用,不适合高频场合
- 安全性差:易突破Java的封装性(private/protected),可能引入风险
- 代码可读性差:反射代码不直观,出错难排查
八、反射的最佳实践
- 仅在确需灵活处理时使用,常规代码避免滥用反射。
- 频繁访问的类/方法建议缓存反射对象(如Method、Field)。
- 配合泛型、注解、设计模式,提高代码通用性与解耦性。
- Spring等主流框架大量使用反射,但都兼顾了性能优化和安全加固措施。