抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

反射是Java的强大特性,允许程序在运行时检查和操作类、方法、字段。Spring、MyBatis等框架的核心都依赖反射。本文系统介绍反射的用法和原理。

什么是反射

反射(Reflection)允许程序在运行时

  • 获取任意类的信息(类名、方法、字段、注解等)
  • 创建任意类的实例
  • 调用任意方法(包括私有方法)
  • 访问和修改任意字段(包括私有字段)
1
2
3
4
5
6
7
8
9
// 正常方式:编译时确定
Person p = new Person();
p.setName("Tom");

// 反射方式:运行时确定
Class<?> clazz = Class.forName("com.example.Person");
Object obj = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getMethod("setName", String.class);
method.invoke(obj, "Tom");

获取Class对象

反射的入口是 Class 对象,有三种获取方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 方式1:通过类名.class
Class<Person> clazz1 = Person.class;

// 方式2:通过对象.getClass()
Person p = new Person();
Class<?> clazz2 = p.getClass();

// 方式3:通过Class.forName()(最灵活,类名可以是变量)
Class<?> clazz3 = Class.forName("com.example.Person");

// 三种方式获取的是同一个Class对象
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz2 == clazz3); // true

获取类信息

基本信息

1
2
3
4
5
6
7
8
Class<?> clazz = Person.class;

clazz.getName(); // com.example.Person(全限定名)
clazz.getSimpleName(); // Person(简单类名)
clazz.getPackageName(); // com.example
clazz.getSuperclass(); // class java.lang.Object
clazz.getInterfaces(); // 实现的接口数组
clazz.getModifiers(); // 修饰符(public, abstract等)

获取构造函数

1
2
3
4
5
6
7
8
// 获取所有public构造函数
Constructor<?>[] constructors = clazz.getConstructors();

// 获取所有构造函数(包括private)
Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();

// 获取指定参数的构造函数
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);

获取方法

1
2
3
4
5
6
7
8
// 获取所有public方法(包括继承的)
Method[] methods = clazz.getMethods();

// 获取本类声明的所有方法(包括private,不包括继承的)
Method[] declaredMethods = clazz.getDeclaredMethods();

// 获取指定方法
Method method = clazz.getDeclaredMethod("setName", String.class);

获取字段

1
2
3
4
5
6
7
8
// 获取所有public字段
Field[] fields = clazz.getFields();

// 获取本类声明的所有字段(包括private)
Field[] declaredFields = clazz.getDeclaredFields();

// 获取指定字段
Field field = clazz.getDeclaredField("name");

创建实例

1
2
3
4
5
6
7
8
9
10
11
12
Class<?> clazz = Class.forName("com.example.Person");

// 方式1:无参构造
Object obj1 = clazz.getDeclaredConstructor().newInstance();

// 方式2:有参构造
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
Object obj2 = constructor.newInstance("Tom", 25);

// 如果构造函数是private的
constructor.setAccessible(true); // 突破访问限制
Object obj3 = constructor.newInstance("Jerry", 30);

调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Class<?> clazz = Person.class;
Object person = clazz.getDeclaredConstructor().newInstance();

// 调用public方法
Method setName = clazz.getMethod("setName", String.class);
setName.invoke(person, "Tom");

// 调用private方法
Method privateMethod = clazz.getDeclaredMethod("secretMethod");
privateMethod.setAccessible(true); // 突破private限制
Object result = privateMethod.invoke(person);

// 调用静态方法
Method staticMethod = clazz.getMethod("staticMethod");
staticMethod.invoke(null); // 静态方法传null

访问和修改字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Class<?> clazz = Person.class;
Object person = clazz.getDeclaredConstructor().newInstance();

// 获取字段
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // private字段需要这一步

// 读取字段值
String name = (String) nameField.get(person);

// 修改字段值
nameField.set(person, "Jerry");

// 修改final字段(Java 9+可能失败)
Field finalField = clazz.getDeclaredField("id");
finalField.setAccessible(true);
finalField.set(person, 100);

处理泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
class Container<T> {
private List<String> names;
}

// 获取字段的泛型类型
Field field = Container.class.getDeclaredField("names");
Type genericType = field.getGenericType();

if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
System.out.println(pt.getRawType()); // interface java.util.List
System.out.println(pt.getActualTypeArguments()[0]); // class java.lang.String
}

处理注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface MyAnnotation {
String value();
}

class Person {
@MyAnnotation("test")
private String name;
}

// 获取字段上的注解
Field field = Person.class.getDeclaredField("name");
MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
System.out.println(annotation.value()); // test

// 检查是否有某注解
boolean hasAnnotation = field.isAnnotationPresent(MyAnnotation.class);

动态代理

反射的重要应用是创建动态代理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface UserService {
void save(String name);
}

// 创建动态代理
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
(proxyObj, method, args) -> {
System.out.println("Before: " + method.getName());
// 这里可以调用真实对象的方法
System.out.println("After: " + method.getName());
return null;
}
);

proxy.save("Tom"); // 会触发InvocationHandler

输出:

1
2
Before: save
After: save

反射的应用场景

1. 框架开发

1
2
3
4
5
6
7
8
9
10
// Spring的依赖注入简化示意
public void inject(Object bean) throws Exception {
for (Field field : bean.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(Autowired.class)) {
field.setAccessible(true);
Object dependency = getBean(field.getType());
field.set(bean, dependency);
}
}
}

2. ORM映射

1
2
3
4
5
6
7
8
9
10
// MyBatis结果映射简化示意
public <T> T mapRow(ResultSet rs, Class<T> clazz) throws Exception {
T obj = clazz.getDeclaredConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
Object value = rs.getObject(field.getName());
field.set(obj, value);
}
return obj;
}

3. 序列化/反序列化

1
2
3
4
5
6
7
8
9
10
11
12
// JSON序列化简化示意
public String toJson(Object obj) throws Exception {
StringBuilder sb = new StringBuilder("{");
Field[] fields = obj.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
fields[i].setAccessible(true);
sb.append("\"").append(fields[i].getName()).append("\":");
sb.append("\"").append(fields[i].get(obj)).append("\"");
if (i < fields.length - 1) sb.append(",");
}
return sb.append("}").toString();
}

反射的性能问题

反射比直接调用慢,主要原因:

  1. 无法内联优化:JIT编译器无法优化反射调用
  2. 安全检查:每次调用都要进行访问权限检查
  3. 参数装箱invoke() 的参数是 Object[],基本类型需要装箱

性能优化建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. 缓存Class、Method、Field对象
private static final Method cachedMethod;
static {
cachedMethod = Person.class.getMethod("getName");
cachedMethod.setAccessible(true); // 提前设置,避免重复检查
}

// 2. 使用MethodHandle(Java 7+,性能更好)
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = lookup.findVirtual(Person.class, "getName",
MethodType.methodType(String.class));
String name = (String) handle.invoke(person);

// 3. 高频调用考虑代码生成(如CGLIB、ByteBuddy)

反射的限制

限制 说明
性能开销 比直接调用慢10-100倍
安全限制 Java 9+模块系统限制了跨模块反射
类型安全 编译器无法检查,运行时才报错
代码可读性 反射代码难以理解和维护

总结

API 用途
Class.forName() 根据类名获取Class对象
clazz.getDeclaredConstructor() 获取构造函数
clazz.getDeclaredMethod() 获取方法
clazz.getDeclaredField() 获取字段
setAccessible(true) 突破private限制
constructor.newInstance() 创建实例
method.invoke() 调用方法
field.get() / set() 读写字段

反射是把双刃剑:

  • 优点:极大的灵活性,是框架的基石
  • 缺点:性能差、破坏封装、类型不安全

使用原则:框架可以用,业务代码尽量避免