一、什么是反射?
反射(Reflection) 是 Java 提供的一种在运行时动态获取类信息并操作对象的机制。
📌 核心能力:
在运行时获取任意类的
Class对象;动态创建对象实例;
获取/调用类的字段(Field)、方法(Method)、构造器(Constructor);
突破访问控制(如访问私有成员);
分析泛型、注解等元数据。
✅ 本质:
Java 将“类”也视为一种对象(java.lang.Class),通过它可以在运行时“自省”(Introspection)和“操控”程序结构。
⚠️ 注意:反射是“双刃剑”——强大但有代价(性能、安全、可维护性)。
二、反射的核心 API(位于 java.lang.reflect 包)
三、获取 Class 对象的三种方式
// 1. 通过对象的 getClass() 方法(运行时)
String str = "Hello";
Class<?> clazz1 = str.getClass();
// 2. 通过类字面量(编译期已知,推荐!)
Class<String> clazz2 = String.class;
// 3. 通过 Class.forName()(常用于动态加载,需处理异常)
try {
Class<?> clazz3 = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}✅ 最佳实践:优先使用
String.class,避免异常,性能更好。
四、反射的典型用法
1. 创建对象实例
public class Person {
private String name;
private int age;
// 私有构造器
private Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
// 使用反射创建对象
public class ReflectionExample {
public static void main(String[] args) throws Exception {
Class<Person> clazz = Person.class;
// 获取私有构造器(参数类型为 String 和 int)
Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class, int.class);
// 设置可访问(突破 private 限制)
constructor.setAccessible(true);
// 创建实例
Person person = constructor.newInstance("张三", 25);
System.out.println(person); // Person{name='张三', age=25}
}
}2. 操作私有字段(Field)
// 修改私有字段
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 允许访问私有成员
// 为 person 对象的 name 字段赋值
nameField.set(person, "李四");
// 读取字段值
String newName = (String) nameField.get(person);
System.out.println(newName); // 李四3. 调用私有方法(Method)
// 在 Person 类中添加一个私有方法
class Person {
// ... 上文字段和构造器 ...
private void sayHello(String message) {
System.out.println(name + " says: " + message);
}
}
// 反射调用
Method sayHelloMethod = clazz.getDeclaredMethod("say_hello", String.class); // 注意方法名拼写
sayHelloMethod.setAccessible(true);
sayHelloMethod.invoke(person, "你好,反射!");
// 输出: 李四 says: 你好,反射!🔍 注意:
getDeclaredMethod只获取当前类声明的方法,不包括父类;若需父类方法,用getMethod()(但只能获取 public 方法)。
4. 获取类的完整信息
Class<?> clazz = ArrayList.class;
// 获取类名
System.out.println("类名: " + clazz.getName()); // java.util.ArrayList
// 获取所有 public 方法(包括继承的)
Method[] publicMethods = clazz.getMethods();
for (Method m : publicMethods) {
System.out.println("Public Method: " + m.getName());
}
// 获取本类声明的所有字段(包括 private)
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
System.out.println("Declared Field: " + f.getName());
}
// 获取实现的接口
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> itf : interfaces) {
System.out.println("Implements: " + itf.getName());
}
// 获取父类
Class<?> superclass = clazz.getSuperclass();
System.out.println("Superclass: " + superclass.getName());5. 操作泛型与注解(高级用法)
示例:获取字段的泛型类型
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
class Student {
private List<String> courses; // 泛型字段
}
// 获取泛型信息
Field field = Student.class.getDeclaredField("courses");
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
Type[] actualTypes = pt.getActualTypeArguments();
System.out.println("泛型类型: " + actualTypes[0]); // class java.lang.String
}示例:读取注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Column {
String name();
}
class User {
@Column(name = "user_name")
private String username;
}
// 读取注解
Field field = User.class.getDeclaredField("username");
if (field.isAnnotationPresent(Column.class)) {
Column col = field.getAnnotation(Column.class);
System.out.println("数据库列名: " + col.name()); // user_name
}✅ 注解必须设置
@Retention(RetentionPolicy.RUNTIME)才能在运行时通过反射获取。
五、反射的扩展应用场景
1. 框架底层实现
Spring:通过反射创建 Bean、注入依赖、处理
@Autowired。Hibernate / MyBatis:映射数据库字段到 Java 对象(ORM)。
JUnit:通过反射查找并执行
@Test方法。Gson / Jackson:通过反射序列化/反序列化 JSON。
2. 动态代理基础
java.lang.reflect.Proxy基于反射实现接口代理。AOP(面向切面编程)常用此机制。
3. 插件系统 / 热加载
动态加载 JAR 包中的类并实例化(如
Class.forName("com.xxx.Plugin"))。
4. 通用工具类
如
BeanUtils.copyProperties()(Apache Commons)利用反射复制对象属性。
六、反射的缺点与注意事项
✅ 建议:
仅在必要时使用反射(如框架开发);
避免在高频路径(如循环)中使用;
缓存
Method、Field等反射对象;优先使用标准 API(如
Map、Function)替代反射。