Java 反射机制

一、反射机制核心概念

反射(Reflection) 允许程序在运行时动态:

  • 获取类的完整结构(类名、方法、字段、注解等)
  • 创建对象
  • 调用方法
  • 操作字段(包括私有成员)
  • 绕过泛型检查

核心类

  • Class:类的元数据
  • Constructor:构造函数
  • Method:方法
  • Field:字段
  • Modifier:访问修饰符解析工具

二、反射核心操作详解

1. 获取 Class 对象(三种方式)

// 方式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. 创建对象(实例化)

// 无参构造创建对象
Class clazz = Class.forName("com.example.User");
Object user1 = clazz.newInstance(); // 已过时,推荐使用下面的方法

// 有参构造创建对象(更安全)
Constructor constructor = clazz.getConstructor(String.class, int.class);
Object user2 = constructor.newInstance("张三", 25);

3. 调用方法

class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}

// 反射调用方法
Class clazz = Calculator.class;
Object calculator = clazz.newInstance();

Method addMethod = clazz.getMethod("add", int.class, int.class);
int result = (int) addMethod.invoke(calculator, 3, 5); // 输出: 8

4. 操作字段(含私有字段)

class Person {
    private String name = "默认姓名";
}

// 反射访问私有字段
Person person = new Person();
Class clazz = person.getClass();

Field field = clazz.getDeclaredField("name");
field.setAccessible(true); // 关键:解除私有访问限制

// 读取字段值
String nameValue = (String) field.get(person); // "默认姓名"

// 修改字段值
field.set(person, "李四");
System.out.println(person.getName()); // 假设有getter方法,输出: "李四"

5. 绕过泛型检查(修改集合类型)

List list = new ArrayList<>();
list.add("合法数据");

// 通过反射插入非String类型
Method addMethod = list.getClass().getMethod("add", Object.class);
addMethod.invoke(list, 100); // 插入整数

System.out.println(list); // 输出: [合法数据, 100]

三、反射高级特性

1. 获取注解信息

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    String value();
}

@MyAnnotation("测试注解")
class MyClass {}

// 读取类上的注解
Class clazz = MyClass.class;
MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
System.out.println(annotation.value()); // 输出: 测试注解

2. 动态代理(结合反射)

interface Animal {
    void speak();
}

class Dog implements Animal {
    public void speak() {
        System.out.println("汪汪!");
    }
}

// 动态代理实现
Animal proxy = (Animal) Proxy.newProxyInstance(
    Dog.class.getClassLoader(),
    new Class[]{Animal.class},
    (obj, method, args) -> {
        System.out.println("方法调用前日志");
        return method.invoke(new Dog(), args);
    }
);

proxy.speak(); // 输出: 方法调用前日志 → 汪汪!

四、反射性能优化

反射调用比直接调用慢100倍以上,优化方案:

  1. 缓存反射对象:重复使用的 Method/Field 存入Map

    private static Map methodCache = new HashMap<>();
    
    public static Method getCachedMethod(Class clazz, String methodName, Class... paramTypes) {
        String key = clazz.getName() + "#" + methodName;
        return methodCache.computeIfAbsent(key, k -> clazz.getMethod(methodName, paramTypes));
    }
    
  2. 使用MethodHandle(Java 7+):接近直接调用的性能

    MethodHandles.Lookup lookup = MethodHandles.lookup();
    MethodHandle handle = lookup.findVirtual(String.class, "length", MethodType.methodType(int.class));
    int length = (int) handle.invokeExact("Hello"); // 输出: 5
    

  3. 避免重复安全检查:通过setAccessible(true)禁用访问检查

    Field field = clazz.getDeclaredField("secret");
    field.setAccessible(true); // 后续访问不再检查权限
    


五、反射的典型应用场景

  1. 框架开发:Spring的IoC容器、Hibernate的ORM映射
  2. 动态代理:AOP实现、RPC客户端
  3. 注解处理器:Lombok、JUnit
  4. 通用工具:JSON序列化/反序列化(Jackson/Gson)
  5. 插件系统:动态加载外部JAR

六、反射的注意事项

  1. 性能开销:避免在频繁执行的代码中使用反射
  2. 安全限制:受安全管理器(SecurityManager)管控
  3. 破坏封装:可访问私有成员,需谨慎使用
  4. 兼容性问题:类结构变化会导致反射代码失效

七、完整示例:通过反射实现对象转Map

public static Map objectToMap(Object obj) throws Exception {
    Map map = new HashMap<>();
    Class clazz = obj.getClass();
    
    // 获取所有字段(包括私有)
    for (Field field : clazz.getDeclaredFields()) {
        field.setAccessible(true);
        map.put(field.getName(), field.get(obj));
    }
    return map;
}

// 测试
class User {
    private String name = "张三";
    public int age = 25;
}

User user = new User();
Map map = objectToMap(user);
System.out.println(map); // 输出: {name=张三, age=25}

你可能感兴趣的:(java,开发语言,spring,spring,cloud)