Java 反射机制深度剖析:原理、用法与实战

Java 反射机制深度剖析:原理、用法与实战

一、引言:为什么要学习 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.List8.3 反射注解代码语言:javascript代码运行次数:0运行复制java复制编辑@Retention(RetentionPolicy.RUNTIME)

@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 优化性能