Java反射学习指南

Java反射学习指南

一、反射基础概念

1.1 什么是反射?

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

  • 获取类的完整结构信息
  • 动态创建对象
  • 动态调用方法
  • 访问和修改字段值
  • 处理注解

1.2 核心类

java 复制代码
// 获取类信息
Class<?> clazz = Class.forName("完整类名");

// 创建对象
Constructor<?> constructor = clazz.getConstructor();
Object obj = constructor.newInstance();

// 调用方法
Method method = clazz.getMethod("方法名", 参数类型.class);
method.invoke(obj, 参数);

// 访问字段
Field field = clazz.getField("字段名");
field.set(obj, 值);

二、详细使用示例

2.1 获取Class对象的三种方式

java 复制代码
public class ReflectionDemo {
    public static void main(String[] args) throws Exception {
        // 方式1: 类名.class
        Class<String> stringClass = String.class;
        
        // 方式2: 对象.getClass()
        String str = "Hello";
        Class<? extends String> strClass = str.getClass();
        
        // 方式3: Class.forName()
        Class<?> clazz = Class.forName("java.lang.String");
        
        System.out.println(stringClass == strClass); // true
        System.out.println(strClass == clazz);       // true
    }
}

2.2 获取构造方法并创建对象

java 复制代码
public class Person {
    private String name;
    private int age;
    
    public Person() {}
    
    public Person(String name) {
        this.name = name;
    }
    
    private Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // getter/setter省略
}

public class ConstructorExample {
    public static void main(String[] args) throws Exception {
        Class<Person> clazz = Person.class;
        
        // 1. 获取所有公共构造方法
        Constructor<?>[] publicConstructors = clazz.getConstructors();
        
        // 2. 获取所有构造方法(包括私有)
        Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();
        
        // 3. 获取特定构造方法
        Constructor<Person> defaultConstructor = clazz.getConstructor();
        Constructor<Person> stringConstructor = clazz.getConstructor(String.class);
        
        // 4. 创建对象
        Person p1 = defaultConstructor.newInstance();
        Person p2 = stringConstructor.newInstance("张三");
        
        // 5. 访问私有构造方法
        Constructor<Person> privateConstructor = 
            clazz.getDeclaredConstructor(String.class, int.class);
        privateConstructor.setAccessible(true); // 设置可访问
        Person p3 = privateConstructor.newInstance("李四", 25);
    }
}

2.3 获取和调用方法

java 复制代码
public class MethodExample {
    public static void main(String[] args) throws Exception {
        Class<Person> clazz = Person.class;
        Person person = clazz.newInstance();
        
        // 获取公共方法(包括继承的)
        Method[] publicMethods = clazz.getMethods();
        
        // 获取所有声明的方法(不包括继承的)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        
        // 获取特定方法
        Method setNameMethod = clazz.getMethod("setName", String.class);
        Method getNameMethod = clazz.getMethod("getName");
        
        // 调用方法
        setNameMethod.invoke(person, "王五");
        String name = (String) getNameMethod.invoke(person);
        System.out.println("Name: " + name); // 王五
        
        // 调用私有方法
        Method privateMethod = clazz.getDeclaredMethod("privateMethod");
        privateMethod.setAccessible(true);
        privateMethod.invoke(person);
    }
}

2.4 访问和修改字段

java 复制代码
public class FieldExample {
    public static void main(String[] args) throws Exception {
        Class<Person> clazz = Person.class;
        Person person = clazz.newInstance();
        
        // 获取公共字段
        Field[] publicFields = clazz.getFields();
        
        // 获取所有字段(包括私有)
        Field[] allFields = clazz.getDeclaredFields();
        
        // 获取特定字段
        Field nameField = clazz.getDeclaredField("name");
        Field ageField = clazz.getDeclaredField("age");
        
        // 设置字段可访问(对私有字段)
        nameField.setAccessible(true);
        ageField.setAccessible(true);
        
        // 设置字段值
        nameField.set(person, "赵六");
        ageField.set(person, 30);
        
        // 获取字段值
        String name = (String) nameField.get(person);
        int age = (int) ageField.get(person);
        
        System.out.println("Name: " + name + ", Age: " + age);
    }
}

2.5 注解处理

java 复制代码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    String value() default "";
    int version() default 1;
}

@MyAnnotation(value = "测试类", version = 2)
class AnnotatedClass {
    @MyAnnotation("测试方法")
    public void annotatedMethod() {}
}

public class AnnotationExample {
    public static void main(String[] args) throws Exception {
        Class<AnnotatedClass> clazz = AnnotatedClass.class;
        
        // 获取类上的注解
        MyAnnotation classAnnotation = clazz.getAnnotation(MyAnnotation.class);
        if (classAnnotation != null) {
            System.out.println("Class annotation value: " + classAnnotation.value());
            System.out.println("Class annotation version: " + classAnnotation.version());
        }
        
        // 获取方法上的注解
        Method method = clazz.getMethod("annotatedMethod");
        MyAnnotation methodAnnotation = method.getAnnotation(MyAnnotation.class);
        if (methodAnnotation != null) {
            System.out.println("Method annotation value: " + methodAnnotation.value());
        }
    }
}

三、实际应用场景

3.1 动态代理

java 复制代码
interface Service {
    void doSomething();
}

class RealService implements Service {
    public void doSomething() {
        System.out.println("Real service working...");
    }
}

class DynamicProxyHandler implements InvocationHandler {
    private Object target;
    
    public DynamicProxyHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method: " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After method: " + method.getName());
        return result;
    }
}

public class ProxyExample {
    public static void main(String[] args) {
        RealService realService = new RealService();
        Service proxy = (Service) Proxy.newProxyInstance(
            Service.class.getClassLoader(),
            new Class[]{Service.class},
            new DynamicProxyHandler(realService)
        );
        proxy.doSomething();
    }
}

3.2 对象拷贝工具

java 复制代码
public class BeanUtils {
    public static <T> T copyProperties(T source, Class<T> targetClass) 
            throws Exception {
        T target = targetClass.newInstance();
        Class<?> sourceClass = source.getClass();
        
        Field[] sourceFields = sourceClass.getDeclaredFields();
        for (Field sourceField : sourceFields) {
            try {
                Field targetField = targetClass.getDeclaredField(
                    sourceField.getName());
                sourceField.setAccessible(true);
                targetField.setAccessible(true);
                
                Object value = sourceField.get(source);
                targetField.set(target, value);
            } catch (NoSuchFieldException e) {
                // 字段不存在则跳过
            }
        }
        return target;
    }
}

四、性能考虑和最佳实践

4.1 性能优化

java 复制代码
public class ReflectionPerformance {
    // 缓存Class对象
    private static final Map<String, Class<?>> CLASS_CACHE = new HashMap<>();
    
    // 缓存Method对象
    private static final Map<String, Method> METHOD_CACHE = new HashMap<>();
    
    public static Class<?> getCachedClass(String className) throws Exception {
        Class<?> clazz = CLASS_CACHE.get(className);
        if (clazz == null) {
            clazz = Class.forName(className);
            CLASS_CACHE.put(className, clazz);
        }
        return clazz;
    }
    
    public static Object invokeCachedMethod(Object obj, String methodName, 
                                          Object... args) throws Exception {
        String key = obj.getClass().getName() + "." + methodName;
        Method method = METHOD_CACHE.get(key);
        if (method == null) {
            Class<?>[] paramTypes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                paramTypes[i] = args[i].getClass();
            }
            method = obj.getClass().getMethod(methodName, paramTypes);
            METHOD_CACHE.put(key, method);
        }
        return method.invoke(obj, args);
    }
}

4.2 最佳实践

  1. 慎用反射:反射会破坏封装性,降低性能
  2. 使用缓存:缓存Class、Method、Field对象
  3. 处理异常:反射操作会抛出多种检查异常
  4. 考虑安全:setAccessible(true)会破坏封装
  5. 性能测试:在性能敏感场景谨慎使用

五、常见面试题

Q1:反射的优缺点?

优点:

  • 动态性:运行时操作
  • 灵活性:可操作私有成员
  • 框架基础:Spring、Hibernate等框架的核心

缺点:

  • 性能开销:比直接调用慢
  • 安全性问题:可绕过访问控制
  • 维护困难:破坏封装性

Q2:如何提高反射性能?

  1. 缓存反射对象
  2. 使用MethodHandle(Java 7+)
  3. 避免频繁反射调用

Q3:反射能修改final字段吗?

可以,但需要先调用setAccessible(true)

java 复制代码
Field field = clazz.getDeclaredField("finalField");
field.setAccessible(true);
field.set(obj, newValue);

六、学习资源

  1. 官方文档:Java Reflection API
  2. 书籍推荐
    • 《Java核心技术 卷I》
    • 《深入理解Java虚拟机》
  3. 实践项目
    • 实现简单的IOC容器
    • 写一个对象映射工具
    • 实现注解处理器

通过系统学习以上内容,你将掌握Java反射的核心概念和使用技巧,能够在实际开发中合理应用反射技术。

相关推荐
TangKenny2 小时前
jar 命令详解
java·pycharm·jar
Solar20252 小时前
机械制造企业数据采集系统选型指南:从技术挑战到架构实践
java·大数据·服务器·架构·云计算
Mr1ght2 小时前
高并发场景下 JSQLParser 性能瓶颈及替代方案实践
java·数据库·sql
不想写bug呀2 小时前
Redis持久化:RDB与AOF
java·数据库·redis
CV_J9 小时前
安装kibana
java·elasticsearch·spring cloud·docker·容器
码农水水10 小时前
国家电网Java面试被问:TCP的BBR拥塞控制算法原理
java·开发语言·网络·分布式·面试·wpf
qq_3363139311 小时前
java基础-网络编程-TCP
java·网络·tcp/ip
咕噜咕噜啦啦11 小时前
Java期末习题速通
java·开发语言
盐真卿12 小时前
python2
java·前端·javascript