一、引言:为什么要学习 Java 反射?Java 是一种强类型语言,一般在编译期确定类型。但在某些场景中,我们希望在运行时动态获取类的信息,甚至动态创建对象、调用方法或访问字段。
✅ 这就是反射机制的价值所在。
常见应用场景包括:
框架开发(如 Spring、MyBatis)
动态代理与 AOP
序列化与反序列化
工具类通用处理
二、反射的核心类与结构Java 反射的核心在于 java.lang.reflect 包,它包括:
类名/接口
作用描述
Class
表示类或接口的运行时类型
Field
表示类的成员变量
Method
表示类的方法
Constructor
表示类的构造方法
Modifier
用于解析修饰符(如 public)
Class 类图结构概览代码语言:javascript代码运行次数:0运行复制bash复制编辑Object
└── Class
├── getName()
├── getFields()/getDeclaredFields()
├── getMethods()/getDeclaredMethods()
├── getConstructors()/getDeclaredConstructors()三、获取 Class 对象的三种方式代码语言:javascript代码运行次数:0运行复制java复制编辑Class> c1 = Class.forName("java.util.ArrayList"); // 方式1:通过类名字符串
Class> c2 = ArrayList.class; // 方式2:通过类.class
Class> c3 = new ArrayList<>().getClass(); // 方式3:通过对象实例✅ 建议使用 Class.forName() 可实现动态加载
四、通过反射创建对象代码语言:javascript代码运行次数:0运行复制java复制编辑Class> clazz = Class.forName("java.util.Date");
Object obj = clazz.getDeclaredConstructor().newInstance();
System.out.println(obj); // 输出当前日期🔒 如果是私有构造器,需要设置访问权限:
代码语言:javascript代码运行次数:0运行复制java复制编辑Constructor> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true); // 打破封装
Object obj = constructor.newInstance();五、访问成员变量(Field)代码语言:javascript代码运行次数:0运行复制java复制编辑class Person {
private String name = "Tom";
}
public class ReflectField {
public static void main(String[] args) throws Exception {
Class> clazz = Person.class;
Object person = clazz.getDeclaredConstructor().newInstance();
Field field = clazz.getDeclaredField("name");
field.setAccessible(true); // 解除私有封装
System.out.println("原值: " + field.get(person)); // Tom
field.set(person, "Jerry");
System.out.println("新值: " + field.get(person)); // Jerry
}
}六、调用方法(Method)代码语言:javascript代码运行次数:0运行复制java复制编辑class Hello {
public void greet(String name) {
System.out.println("Hello, " + name);
}
}代码语言:javascript代码运行次数:0运行复制java复制编辑Class> clazz = Hello.class;
Object hello = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getMethod("greet", String.class);
method.invoke(hello, "Reflect");七、处理构造方法(Constructor)代码语言:javascript代码运行次数:0运行复制java复制编辑class Student {
public Student(String name) {
System.out.println("Student: " + name);
}
}
Class> clazz = Student.class;
Constructor> constructor = clazz.getConstructor(String.class);
Object obj = constructor.newInstance("Lucy");八、反射数组、泛型与注解8.1 反射数组类型代码语言:javascript代码运行次数:0运行复制java复制编辑int[] arr = {1, 2, 3};
Class> arrClass = arr.getClass();
System.out.println(arrClass.isArray()); // true
System.out.println(arrClass.getComponentType()); // int8.2 反射泛型类型参数(带有类型擦除)代码语言:javascript代码运行次数:0运行复制java复制编辑Field field = MyClass.class.getDeclaredField("list");
Type genericType = field.getGenericType();
System.out.println(genericType); // java.util.List
@interface MyAnno {
String value();
}
@MyAnno("Test")
class Target {}
Annotation[] annos = Target.class.getAnnotations();九、实战案例:通用 Bean 工具类9.1 通用拷贝工具(类似 BeanUtils)代码语言:javascript代码运行次数:0运行复制java复制编辑public static void copy(Object src, Object dest) throws Exception {
Class> srcClz = src.getClass();
Class> destClz = dest.getClass();
for (Field field : srcClz.getDeclaredFields()) {
field.setAccessible(true);
Object value = field.get(src);
Field destField = destClz.getDeclaredField(field.getName());
destField.setAccessible(true);
destField.set(dest, value);
}
}使用示例:
代码语言:javascript代码运行次数:0运行复制java复制编辑Person p1 = new Person("Tom", 18);
Person p2 = new Person();
copy(p1, p2); // 将 p1 的字段值复制到 p2十、反射的性能问题与优化10.1 性能开销大的原因: 多次方法查找(Method、Field)
访问权限检查
动态调用机制(invoke)
10.2 优化建议:方法
描述
缓存 Class/Method/Field
使用 Map 缓存反射对象
使用 setAccessible(true)
减少安全性检查开销
使用 Lambda 表达式替代反射
通过函数式接口提升性能
十一、JDK 动态代理的底层实现依赖反射示例:代码语言:javascript代码运行次数:0运行复制java复制编辑interface Service {
void run();
}
class ServiceImpl implements Service {
public void run() {
System.out.println("业务执行...");
}
}代码语言:javascript代码运行次数:0运行复制java复制编辑Service proxy = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class[]{Service.class},
(proxyObj, method, args) -> {
System.out.println("前置逻辑");
Object result = method.invoke(new ServiceImpl(), args);
System.out.println("后置逻辑");
return result;
}
);
proxy.run();🔍 method.invoke(...) 就是反射的核心!
十二、常见反射异常与处理异常类型
说明
ClassNotFoundException
类路径错误,未找到 class 文件
NoSuchMethodException
方法名或参数类型不匹配
IllegalAccessException
私有成员未设置访问权限
InvocationTargetException
方法内部抛出异常
建议统一用 try-catch 处理异常,保证稳定性。
十三、反射 vs 非反射:实测对比代码语言:javascript代码运行次数:0运行复制java复制编辑public class Test {
public void call() {}
}
@Test
public void testSpeed() {
Test t = new Test();
long t1 = System.nanoTime();
for (int i = 0; i < 1000000; i++) t.call();
long t2 = System.nanoTime();
Method method = t.getClass().getMethod("call");
for (int i = 0; i < 1000000; i++) method.invoke(t);
long t3 = System.nanoTime();
System.out.println("普通调用: " + (t2 - t1));
System.out.println("反射调用: " + (t3 - t2));
} 📉 结论:反射调用性能一般为普通调用的 10 倍以上
十四、图解反射执行流程代码语言:javascript代码运行次数:0运行复制vbnet复制编辑用户代码 -> 通过 Class.forName 加载类信息
↓
获得 Class 对象
↓
通过反射获取字段/方法/构造器
↓
设置 setAccessible(true)
↓
动态调用或修改对象属性十五、总结与建议Java 反射机制为运行时动态操作提供强大能力,在多种框架中扮演核心角色。
优势
局限性
高灵活性
性能较低
支持框架封装与通用性
安全性风险(破坏封装)
实现动态代理、IOC、ORM 等
可读性差,调试难度大
建议使用原则: 🚫 普通业务尽量不用反射
✅ 框架、工具类、通用层可以使用
📦 注意使用缓存和 setAccessible 优化性能