反射(Reflection)是Java语言的一个强大特性,它允许程序在运行时动态地获取类的信息并操作类或对象的属性、方法和构造器。就是在获取运行时的java字节码文件,通过各种方法去创建对象,反射是Java被视为动态语言的关键特性之一。
反射其实就是获取到内存的class对象,而class对象是类加载器加载了字节码文件转换过来的。

一、反射基础概念
1. 什么是反射
反射是指在程序运行期间,能够动态地获取类的信息(如类名、方法、字段、构造器等)并动态调用对象方法或修改属性的能力。
2. 反射的核心类
Java反射主要涉及以下核心类:
-
Class
:表示类的实体 -
Field
:表示类的成员变量 -
Method
:表示类的方法 -
Constructor
:表示类的构造方法 -
Array
:提供了动态创建和访问数组的方法
二、反射的基本使用
1. 获取Class对象的三种方式
java
// 1. 通过类名.class
Class<?> clazz1 = String.class;
// 2. 通过对象.getClass()
String str = "Hello";
Class<?> clazz2 = str.getClass();
// 3. 通过Class.forName()
Class<?> clazz3 = Class.forName("java.lang.String");
2. 创建对象
java
// 使用无参构造器
Class<?> clazz = Class.forName("com.example.User");
User user = (User) clazz.newInstance(); // 已废弃,建议使用getDeclaredConstructor().newInstance()
// 使用有参构造器
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
User user = (User) constructor.newInstance("张三", 25);
3. 获取和操作字段
java
Class<?> clazz = User.class;
User user = new User("李四", 30);
// 获取public字段
Field nameField = clazz.getField("name"); // 只能获取public字段
nameField.set(user, "王五");
// 获取所有字段(包括private)
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true); // 设置可访问private字段
ageField.set(user, 35);
4. 调用方法
java
Class<?> clazz = User.class;
User user = new User();
// 获取public方法
Method publicMethod = clazz.getMethod("publicMethod", String.class);
publicMethod.invoke(user, "参数");
// 获取所有方法(包括private)
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(user);
三、反射的高级特性
1. 动态代理
反射常用于实现动态代理:
java
interface Subject {
void request();
}
class RealSubject implements Subject {
public void request() {
System.out.println("真实请求");
}
}
class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理前操作");
Object result = method.invoke(target, args);
System.out.println("代理后操作");
return result;
}
}
// 使用
Subject realSubject = new RealSubject();
Subject proxy = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
new DynamicProxy(realSubject)
);
proxy.request();
2. 注解处理
反射可以用于处理运行时注解:
java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyAnnotation {
String value();
}
class AnnotationProcessor {
public void process(Object obj) throws Exception {
for (Method method : obj.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println("发现注解方法: " + method.getName() +
", 注解值: " + annotation.value());
method.invoke(obj);
}
}
}
}
3. 泛型类型擦除后的类型获取
虽然Java在运行时擦除了泛型类型信息,但可以通过反射获取字段或方法的泛型签名:
java
class GenericClass<T> {
private List<String> stringList;
public void setValue(T value) {}
}
// 获取字段的泛型类型
Field field = GenericClass.class.getDeclaredField("stringList");
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) type;
Type[] actualTypes = pType.getActualTypeArguments(); // [String]
}
// 获取方法的泛型参数类型
Method method = GenericClass.class.getMethod("setValue", Object.class);
Type[] paramTypes = method.getGenericParameterTypes();
四、反射的性能考虑
反射虽然强大,但也有一些缺点:
-
性能开销:反射操作比直接调用慢,因为涉及动态类型解析和方法调用验证
-
安全限制:反射需要运行时权限,可能被安全管理器限制
-
破坏封装:可以访问私有成员,破坏了面向对象的封装性
性能优化建议:
-
缓存Class对象、Method对象等反射结果
-
对于频繁调用的方法,可以设置
setAccessible(true)
减少访问检查 -
考虑使用MethodHandle(Java 7+)作为替代方案
五、反射的应用场景
-
框架开发:Spring、Hibernate等框架大量使用反射
-
动态代理:AOP编程的基础
-
IDE开发:如代码提示、自动补全
-
测试工具:如JUnit通过反射调用测试方法
-
序列化/反序列化:如JSON/XML解析库
-
插件系统:动态加载和调用插件
六、反射的替代方案
在Java 7+中,可以使用MethodHandle
作为反射的替代方案,它提供了更好的性能:
java
class MethodHandleDemo {
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType type = MethodType.methodType(void.class, String.class);
MethodHandle handle = lookup.findVirtual(User.class, "publicMethod", type);
User user = new User();
handle.invokeExact(user, "MethodHandle调用");
}
}