一,什么是反射?
反射(Reflection) 是 Java 在运行时检视自身的能力。它允许程序在运行时:
- 获取类的完整信息
- 构造对象
- 调用方法
- 访问/修改字段
- 甚至绕过访问权限控制
// 没有反射:一切都必须在编译时确定
User user = new User(); // 编译时就知道是User类
user.setName("张三"); // 编译时就知道有setName方法
// 有反射:运行时才决定
Class<?> clazz = Class.forName("com.example.User"); // 运行时才知道是哪个类
Object obj = clazz.newInstance(); // 运行时创建对象
Method method = clazz.getMethod("setName", String.class); // 运行时找方法
method.invoke(obj, "张三"); // 运行时调用
二,获取class对象

获取 Class 对象的三种方式
// 方式1:类名.class(最常用,编译时检查)
Class<User> clazz1 = User.class;
// 方式2:对象.getClass()
User user = new User();
Class<? extends User> clazz2 = user.getClass();
// 方式3:Class.forName()(最灵活,运行时加载)
Class<?> clazz3 = Class.forName("com.example.User");
三,获取类中信息
利用 class 类获取类中的信息方法:
- 通过Constructor,field,method判断这个方法是获取构造方法,成员变量,成员方法。
- 如果加上了Declared代表获取所有的类型的东西
- 最后末尾加上s代表获取所有。
- 一般格式为
get+Delcared(可以不加,不加代表只获得公共的)+Constructor/field/method+s(这个s可以不加代表获取指定的)
反射 API 命名规律速查表
| API格式 | 含义 | 示例 |
|---|---|---|
| getXxx | 获取公共的Xxx | getMethod() |
| getDeclaredXxx | 获取所有的Xxx(包括私有) | getDeclaredMethod() |
| getXxxs | 获取所有公共的Xxx | getMethods() |
| getDeclaredXxxs | 获取所有的Xxx | getDeclaredMethods() |
3.1 反射获取构造方法

代码举例:
Student 类
package domain;
public class Student {
private int age;
private String name;
public Student() {
System.out.println("无参构造器执行了~")
}
public Student(String name, int age) {
System.out.println("有参构造器执行了~")
this.name = name;
this.age = age;
}
}
测试类:
@Test
public void testRelfct() throws NoSuchMethodException {
Class<Student> studentClass = Student.class;
//1.获取全部public修饰的构造器
Constructor<?>[] constructors1 = studentClass.getConstructors();
//2.获取全部的构造器
Constructor<?>[] constructors2 = studentClass.getDeclaredConstructors();
//3.获取public修饰的指定构造器
//获取指定public修饰的无参构造
Constructor<Student> constructor1 = studentClass.getConstructor();
//获取指定public修饰的有参构造
Constructor<Student> constructor2 = studentClass.getConstructor(String.class,int.class);
//获取指定无参构造
Constructor<Student> declaredConstructor1 = studentClass.getDeclaredConstructor();
//获取指定有参构造
Constructor<Student> declaredConstructor2 = studentClass.getDeclaredConstructor(String.class,int.class);
}
获取类构造器的作用:依然是初始化对象返回

@Test
public void testRelfct() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<Student> studentClass = Student.class;
//获取指定无参构造(反射一般不会填写数据类型,为了确保代码的通用性)
Constructor<Student> declaredConstructor1 = studentClass.getDeclaredConstructor();
//获取指定有参构造(反射一般不会填写数据类型,为了确保代码的通用性)
Constructor<Student> declaredConstructor2 = studentClass.getDeclaredConstructor(String.class,int.class);
//调用构造器创建对象(如果构造器是private修饰的就必须设置为可访问)
declaredConstructor1.setAccessible(true);
Student student = declaredConstructor1.newInstance();
Student student1 = declaredConstructor2.newInstance("张三", 18);
}
3.2 反射获取字段(成员变量)

代码举例:
Student 类
package domain;
public class Student {
public static int a;
public static final String COUNTRY="中国";
private int age;
private String name;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
测试类:
@Test
public void testRelfct() throws NoSuchFieldException {
Class<Student> studentClass = Student.class;
//1.获取全部public修饰的成员变量
Field[] fields = studentClass.getFields();
//2.获取全部成员变量
Field[] declaredFields = studentClass.getDeclaredFields();
//3.获取指定public修饰的成员变量
Field name = studentClass.getField("name");
//4.获取指定成员变量
Field age = studentClass.getDeclaredField("age");
}
获取到成员变量的作用:依然是赋值、取值。

@Test
public void testRelfct() throws NoSuchFieldException, IllegalAccessException {
Class<Student> studentClass = Student.class;
Field name = studentClass.getDeclaredField("name");
Student s=new Student("张三",13);
System.out.println(s.getName()+":"+s.getAge());
name.setAccessible(true);//禁止访问权限检查,进行暴力反射
name.set(s,"李四");//将对象s的name属性设置为"李四"
System.out.println(s.getName()+":"+s.getAge());
System.out.println(name.get(s));//获取对象s的name属性的值
}
3.3 反射获取成员方法

代码举例:
Student 类
package domain;
public class Student {
public static int a;
public static final String COUNTRY="中国";
private int age;
private String name;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃");
}
public void eat(String name){
System.out.println(name+"吃");
}
private void move(String name){
System.out.println(name+"移动");
}
}
测试类:
@Test
public void testRelfct() throws NoSuchMethodException {
Class<Student> studentClass = Student.class;
//1.获取所有public修饰的方法
Method[] methods = studentClass.getMethods();
//2.获取所有方法
Method[] declaredMethods = studentClass.getDeclaredMethods();
//3.获取指定方法
Method eat1 = studentClass.getMethod("eat");//获取public修饰的无参eat方法
Method eat2 = studentClass.getMethod("eat", String.class);//获取public修饰的有参eat方法
//获取有参eat方法
Method eat3 = studentClass.getDeclaredMethod("eat");//获取public修饰的无参eat方法
Method eat4 = studentClass.getDeclaredMethod("eat", String.class);
}
成员方法的作用:依旧是执行

@Test
public void testRelfct() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<Student> studentClass = Student.class;
Method move = studentClass.getDeclaredMethod("move",String.class);
Student s=new Student("张三",13);
move.setAccessible(true);//设置访问权限,暴力反射
move.invoke(s,"小猫");//调用对象s的move方法
}
四,反射的作用
反射让程序在运行时能「看到」自己的结构,并「操作」这些结构。
就像游戏里的「上帝模式」:能看到所有信息,能修改所有东西。
4.1 动态探查
// 运行时才知道这是什么类,还能查看它的"内部构造"
public void inspectClass(Object obj) {
Class<?> clazz = obj.getClass();
// 看类的基本信息
System.out.println("类名: " + clazz.getName());
System.out.println("父类: " + clazz.getSuperclass().getName());
// 看类的"器官"(字段)
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("字段: " + field.getName() + " - " + field.getType());
}
// 看类的"技能"(方法)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("方法: " + method.getName());
}
// 看类的"身份证"(注解)
Annotation[] annotations = clazz.getAnnotations();
for (Annotation anno : annotations) {
System.out.println("注解: " + anno.annotationType().getName());
}
}
实际应用:
- IDE的代码提示功能
- 调试工具查看对象状态
- 生成API文档
4.2 动态创建
// 工厂不知道具体要造什么,运行时才知道
public Object createInstance(String className) throws Exception {
// 1. 加载类(根据名字找图纸)
Class<?> clazz = Class.forName(className);
// 2. 创建实例(按图纸造人)
return clazz.newInstance();
}
// 使用:传入什么类名就创建什么对象
Object user = createInstance("com.example.User");
Object product = createInstance("com.example.Product");
Object service = createInstance("com.example.Service");
实际应用:
- Spring IoC容器(
@Autowired) - 插件系统(动态加载jar包)
- 配置文件驱动
4.3 动态调用
// 调用未知对象的方法
public Object invokeMethod(Object obj, String methodName, Object... args)
throws Exception {
Class<?> clazz = obj.getClass();
// 1. 找到方法(按名字找技能)
Class<?>[] paramTypes = getParameterTypes(args);
Method method = clazz.getDeclaredMethod(methodName, paramTypes);
// 2. 调用方法(执行技能)
method.setAccessible(true); // 私有方法也能调用
return method.invoke(obj, args);
}
// 使用
User user = new User();
invokeMethod(user, "setName", "张三");
invokeMethod(user, "setAge", 25);
String name = (String) invokeMethod(user, "getName");
实际应用:
- AOP切面编程(Spring AOP)
- 远程方法调用(RPC)
- 测试框架(JUnit调用测试方法)
4.4 动态修改
// 修改对象的内部状态
public void modifyObject(Object obj, Map<String, Object> values)
throws Exception {
Class<?> clazz = obj.getClass();
for (Map.Entry<String, Object> entry : values.entrySet()) {
String fieldName = entry.getKey();
Object value = entry.getValue();
// 找到字段(找到要整的部位)
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true); // 突破private限制
// 修改值(动手术)
field.set(obj, value);
}
}
// 使用:把私有字段name改成"李四"
User user = new User();
Map<String, Object> changes = new HashMap<>();
changes.put("name", "李四");
modifyObject(user, changes); // 即使name是private也能改
实际应用:
- ORM框架设置主键
- 反序列化(JSON转对象)
- Mock测试(修改final字段)
五,反射的性能损失
5.1 性能对比数据
public class ReflectionPerformance {
public static void main(String[] args) throws Exception {
User user = new User("张三", 25);
// 1. 直接调用(基准)
long start = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
String name = user.getName();
}
long directTime = System.nanoTime() - start;
// 2. 反射调用(不缓存)
Method getNameMethod = User.class.getMethod("getName");
start = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
String name = (String) getNameMethod.invoke(user);
}
long reflectTime = System.nanoTime() - start;
// 3. 反射调用(缓存Method)
// 上面的getNameMethod已经缓存
// 4. 反射调用(缓存 + setAccessible)
getNameMethod.setAccessible(true); // 关闭访问检查
start = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
String name = (String) getNameMethod.invoke(user);
}
long reflectAccessibleTime = System.nanoTime() - start;
System.out.println("=== 性能对比(100万次调用)===");
System.out.println("直接调用: " + directTime / 1000000 + " ms");
System.out.println("反射调用(无缓存): " + reflectTime / 1000000 + " ms");
System.out.println("反射调用(缓存): " + reflectAccessibleTime / 1000000 + " ms");
System.out.println("\n性能比例:");
System.out.println("反射(无缓存) / 直接 = " + reflectTime / directTime + " 倍");
System.out.println("反射(缓存) / 直接 = " + reflectAccessibleTime / directTime + " 倍");
}
}
// 典型输出结果:
// === 性能对比(100万次调用)===
// 直接调用: 5 ms
// 反射调用(无缓存): 320 ms
// 反射调用(缓存): 65 ms
//
// 性能比例:
// 反射(无缓存) / 直接 = 64 倍
// 反射(缓存) / 直接 = 13 倍
结论:最差情况慢60 倍,优化后仍慢10 倍以上!
5.2 反射为什么慢?
反射之所以慢是八大链路导致的
-
安全检查(Security Check)
// 每次反射调用都要做这些检查: Method method = clazz.getMethod("getName"); // invoke()内部要检查: // 1. 权限检查(Can I access this method?) if (!method.isAccessible()) { // 检查调用者是否有权限访问 SecurityManager.checkPermission(new ReflectPermission("suppressAccessChecks")); } // 2. 参数类型检查(Are the arguments correct?) if (!method.getParameterTypes()[0].isInstance(args[0])) { throw new IllegalArgumentException("Argument type mismatch"); } // 3. 返回类型检查(Is the return type correct?) // 4. 异常检查(Does it throw the right exceptions?) // 直接调用时,这些检查在编译时完成,反射在运行时每次都要做! -
方法查找(Method Lookup)
// getMethod()的内部操作 public Method getMethod(String name, Class<?>... parameterTypes) { // 1. 安全检查(每次都要检查权限) checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); // 2. 在方法表中线性搜索(复杂度O(n)) Method method = getMethod0(name, parameterTypes); if (method == null) { throw new NoSuchMethodException(name); } // 3. 返回方法的副本(不是缓存的原件!) return getReflectionFactory().copyMethod(method); } // 直接调用时:方法地址在编译时就确定了 // 0x1234: invokevirtual #5 // Method User.getName:()Ljava/lang/String; // 直接跳转到方法地址,无需查找 -
参数装箱 / 拆箱(Boxing/Unboxing)
public class BoxingCost { public void test(int num) {} // 原始类型 public static void main(String[] args) throws Exception { Method method = BoxingCost.class.getMethod("test", int.class); BoxingCost obj = new BoxingCost(); // 反射调用:需要把int装箱为Integer Object[] params = {100}; // 自动装箱:int → Integer long start = System.nanoTime(); for (int i = 0; i < 1000000; i++) { method.invoke(obj, params); // 内部要拆箱:Integer → int } System.out.println("反射调用: " + (System.nanoTime() - start)); // 直接调用:直接传int start = System.nanoTime(); for (int i = 0; i < 1000000; i++) { obj.test(100); // 直接传递原始类型 } System.out.println("直接调用: " + (System.nanoTime() - start)); } } // 输出:反射调用慢更多,因为多了装箱拆箱开销 -
可变参数处理(Varargs)
// invoke()方法签名:Object invoke(Object obj, Object... args) // 这意味着: // 1. 参数要包装成Object数组 // 2. 数组要分配内存(如果不在栈上分配) // 3. 参数要复制到数组中 // 直接调用:参数直接在栈上传递 obj.method("arg1", 2, 3.0); // 反射调用:参数要装箱成数组 Object[] args = {"arg1", Integer.valueOf(2), Double.valueOf(3.0)}; method.invoke(obj, args); -
异常包装(Exception Wrapping)
try { method.invoke(obj, args); } catch (InvocationTargetException e) { // 反射把原始异常包装了一层 Throwable cause = e.getCause(); throw cause; } // 直接调用的异常: try { obj.method(); } catch (SpecificException e) { // 直接抛出,没有包装 } // 反射的异常处理链: // 1. 调用方法 // 2. 如果抛异常,捕获并包装为InvocationTargetException // 3. 重新抛出 // 多了一层包装和栈追踪 -
无法内联优化(No Inlining)
```java
// JIT编译器优化示例
public class InliningDemo {
public int add(int a, int b) {
return a + b; // 简单方法
}
public static void main(String[] args) {
InliningDemo demo = new InliningDemo();
// 直接调用:JIT可以内联优化
// 编译后可能变成:result = a + b; (没有方法调用开销)
int result = 0;
for (int i = 0; i < 1000000; i++) {
result += demo.add(i, i+1); // 可能被内联
}
// 反射调用:无法内联
Method addMethod = InliningDemo.class.getMethod("add", int.class, int.class);
for (int i = 0; i < 1000000; i++) {
result += (int) addMethod.invoke(demo, i, i+1); // 必须走完整的方法调用
}
}
}
```
7. 内存分配(Memory Allocation)
```java
// 反射调用涉及的对象创建
public class MemoryAllocation {
public static void main(String[] args) throws Exception {
Method method = MemoryAllocation.class.getMethod("test");
MemoryAllocation obj = new MemoryAllocation();
// 每次invoke()可能创建:
// 1. Object[] 参数数组(即使参数为空)
// 2. 包装异常对象
// 3. 方法访问器对象
// HotSpot JVM使用动态生成的MethodAccessor
// 第一次调用:生成NativeMethodAccessorImpl
// 第15次调用:生成GeneratedMethodAccessor1(字节码)
// 这个生成过程本身就很耗时!
}
}
```
8. 调用链更长(Longer Call Chain)
```java
// 直接调用的调用栈
User.getName()
↓
返回结果
// 反射调用的调用栈
Method.invoke()
↓
NativeMethodAccessorImpl.invoke()
↓
GeneratedMethodAccessor1.invoke()
↓
DelegatingMethodAccessorImpl.invoke()
↓
User.getName() // 最终调用实际方法
↓
返回,层层返回
// 多了4层调用栈!
```
六,总结
反射的本质:让程序在运行时不再「盲人摸象」,而是有了「透视眼」和「操控杆」,可以查看和操作自己的内部结构。
反射的价值:实现「动态性」—— 在编译时不知道,运行时才知道要干什么。
反射的代价:性能损失、安全性降低、代码复杂。
最后建议:反射是框架开发者的瑞士军刀,普通开发者的核武器—— 知道怎么用,但别轻易用。