【Java基础整理】Java反射详解

24 阅读11分钟

Java反射详解

1. 反射概述

什么是反射

反射(Reflection)是Java中的一种强大机制,它允许程序在运行时检查和操作类、对象、方法和属性。

核心思想:

  • 将一个Java类看成是一个对象,用Class类型的对象来表示,称为类对象
  • 可以获取这个类对象中的组成部分:变量、方法、注释、修饰符等信息
  • 类中的每个组成部分也可以当做对象,用对应的类实例来表示

反射的核心类

反射中类和类中成员对应的具体类:

类名用途包位置
Class代表一个Java类对象java.lang
Field代表类或接口的单个字段java.lang.reflect
Method代表类中的方法java.lang.reflect
Constructor代表类中的构造方法java.lang.reflect
Array代表数组操作工具java.lang.reflect
Package代表包信息java.lang

定义: 这种将Java类和类中的成员映射成对象并对其操作的机制,就叫做反射

2. Class类详解

Class类型对象代表一个Java类对象,是反射的入口点。

2.1 获取Class对象的三种方法

// 方法1:类名.class
Class<?> class1 = Person.class;

// 方法2:Class.forName("完整类名") - 推荐方式
Class<?> class2 = Class.forName("java.lang.Integer");

// 方法3:对象.getClass()
Class<?> class3 = "abc".getClass();

2.2 Class类主要方法

方法名功能描述
forName(String className)静态方法,获取指定类名的Class对象
newInstance()创建该类的实例对象
getName()获取类的完整名称
getSimpleName()获取类的简单名称
注解相关方法
方法名功能描述
getAnnotation(Class<A> annotationClass)获取指定类型的注解,没有返回null
getAnnotations()获取此元素上存在的所有注解
getDeclaredAnnotations()获取直接在此元素上的所有注解
构造方法相关
方法名功能描述
getDeclaredConstructor(Class<?>... parameterTypes)获取本类声明的指定参数类型的构造方法
getDeclaredConstructors()获取本类所有构造方法
getConstructor(Class<?>... parameterTypes)获取公共构造方法
字段相关
方法名功能描述
getField(String name)获取公共成员字段
getDeclaredField(String name)获取本类声明的指定字段(包括私有字段)
getFields()获取所有公共字段
getDeclaredFields()获取本类声明的所有字段
方法相关
方法名功能描述
getMethod(String name, Class<?>... parameterTypes)获取公共方法
getDeclaredMethod(String name, Class<?>... parameterTypes)获取本类声明的指定方法
getMethods()获取所有公共方法
getDeclaredMethods()获取本类声明的所有方法
其他实用方法
方法名功能描述
getClassLoader()获取类加载器
getComponentType()获取数组组件的Class
getPackage()获取类所属的包
getSuperclass()获取父类
getInterfaces()获取实现的接口
判断方法
方法名功能描述
isAnnotation()判断是否为注解类
isArray()判断是否为数组类
isEnum()判断是否为枚举类
isInterface()判断是否为接口

3. Constructor类详解

Constructor类代表某个类中的一个构造方法。

主要方法

方法名功能描述
newInstance(Object... initargs)调用该构造方法,实例化对象
getAnnotation(Class<A> annotationClass)获取指定类型的注解
getDeclaredAnnotations()获取该元素上的所有注解
getName()获取构造方法名称
getParameterTypes()获取参数类型

4. Field类详解

Field类代表类或接口的单个字段信息。

主要方法

获取字段值
方法名功能描述
get(Object obj)获取指定对象上该字段的值
getBoolean(Object obj)获取boolean类型字段值
getByte(Object obj)获取byte类型字段值
getChar(Object obj)获取char类型字段值
getInt(Object obj)获取int类型字段值
getDouble(Object obj)获取double类型字段值
设置字段值
方法名功能描述
set(Object obj, Object value)给该字段赋值
setBoolean(Object obj, boolean value)设置boolean值
setInt(Object obj, int value)设置int值
setDouble(Object obj, double value)设置double值
其他方法
方法名功能描述
getName()获取字段名称
getType()获取字段类型
getModifiers()获取修饰符
setAccessible(boolean flag)设置是否可访问(用于访问私有字段)

5. Method类详解

Method类提供了对类或接口中方法的描述,代表类中的方法。

常用方法

方法名功能描述
invoke(Object obj, Object... args)调用该方法
getName()获取方法名
getGenericParameterTypes()获取方法的参数类型列表
getModifiers()获取方法修饰符的整数表现形式
getReturnType()获取返回值类型
getParameterTypes()获取参数类型数组
setAccessible(boolean flag)设置是否可访问

重要特性: 可以不用创建对象实例,直接获取某类的方法,调用invoke()来实现该方法的应用。

6. Array类详解

Array类代表数组操作工具类,提供了对数组的反射操作。

主要方法

方法名功能描述
get(Object array, int index)获取数组指定索引的元素
set(Object array, int index, Object value)设置数组指定索引的元素值
getLength(Object array)获取数组长度
newInstance(Class<?> componentType, int length)创建新数组
newInstance(Class<?> componentType, int... dimensions)创建多维数组

7. 反射应用实战

7.1 示例类定义

首先定义一个类似JavaBean的Person类:

package Reflect;

// 定义了类似JavaBean的一个类,下面将对此类进行反射操作
class Person {
    private int age = 0;
    public String name = "unknownName";
    
    public Person() {
        super();
    }
    
    public Person(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return this.name + " " + this.age;
    }
}

7.2 反射应用案例

import java.lang.annotation.Target;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Demo {
    
    // 以下代码演示反射的基本应用
    public static void main(String[] args) throws ClassNotFoundException, 
            InstantiationException, IllegalAccessException, SecurityException, 
            NoSuchMethodException, IllegalArgumentException, 
            InvocationTargetException, NoSuchFieldException {
        
        // 案例0:类反射 - 获取Class对象的三种方式
        System.out.println("=== 案例0:获取Class对象 ===");
        
        // 第一种:类名.class
        Class<?> class1 = Person.class;
        // 第二种:Class.forName("类名") - 推荐使用
        Class<?> class2 = Class.forName("java.lang.Integer");
        // 第三种:对象.getClass()
        Class<?> class3 = (new int[]{1, 3}).getClass();
        
        System.out.println(class1.getName());
        System.out.println(class2.getName());
        System.out.println(class3.getName());
        
        // 案例1:获取实现类的父类和父接口
        System.out.println("\n=== 案例1:获取父类和接口 ===");
        
        Class<?> supers = Class.forName("java.util.Hashtable").getSuperclass();
        System.out.println("父类:" + supers.getName());
        
        Class<?>[] interfaces = Class.forName("java.util.HashSet").getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            System.out.println("接口" + (i + 1) + ": " + interfaces[i].getName());
        }
        
        // 案例2:反射创建对象
        System.out.println("\n=== 案例2:反射创建对象 ===");
        
        // 1. 通过Class直接实例化(调用无参构造)
        Person p1 = (Person) class1.newInstance();
        
        // 2. 通过Constructor创建对象
        Constructor<?> constructor = class1.getConstructor(int.class, 
                Class.forName("java.lang.String"));
        Person p2 = (Person) constructor.newInstance(19, "lisi");
        
        System.out.println(p1);
        System.out.println(p2);
        
        // 案例3:使用反射获取并操作对象属性
        System.out.println("\n=== 案例3:操作对象属性 ===");
        
        Field field = p1.getClass().getDeclaredField("name");
        field.set(p1, "zhangsan");
        System.out.println(Modifier.toString(field.getModifiers()) + " " 
                + field.getType() + " " + field.getName());
        System.out.println(p1);
        
        // 案例4:反射获取方法并调用
        System.out.println("\n=== 案例4:调用方法 ===");
        
        Method method = class1.getMethod("setAge", int.class);
        // 调用方法:p2是要操作的对象,20是给该方法传入的参数
        // 如果调用静态方法,第一个参数可以传null
        method.invoke(p2, 20);
        
        System.out.println(p2);
        
        // 案例5:数组的反射操作
        System.out.println("\n=== 案例5:数组反射操作 ===");
        
        int[] arr = {1, 2, 3};
        // 获取表示数组组件的Class
        Class<?> componentType = arr.getClass().getComponentType();
        System.out.println("数组的类型:" + componentType.getName());
        System.out.println("数组的长度:" + Array.getLength(arr));
        System.out.println("数组的第一个元素:" + Array.get(arr, 0));
        
        // 用newInstance创建数组
        int[] newArr = (int[]) Array.newInstance(componentType, 4);
        // 复制原数组的值
        System.arraycopy(arr, 0, newArr, 0, Array.getLength(arr));
        // 给新数组的第4个位置赋值
        Array.setInt(newArr, 3, 4);
        
        for (int i = 0; i < newArr.length; i++) {
            System.out.print(newArr[i] + " ");
        }
        System.out.println();
        
        // 案例6:注解的反射调用
        System.out.println("\n=== 案例6:注解反射 ===");
        
        // 判断该类是否是注解类
        System.out.println("Demo类是否为注解类:" + Demo.class.isAnnotation());
        
        // 判断该元素上是否有指定注解
        boolean hasTargetAnnotation = Demo.class.getMethod("main", String[].class)
                .isAnnotationPresent(Target.class);
        System.out.println("main方法是否有Target注解:" + hasTargetAnnotation);
    }
}

7.3 运行结果示例

=== 案例0:获取Class对象 ===
Reflect.Person
java.lang.Integer
[I

=== 案例1:获取父类和接口 ===
父类:java.util.Dictionary
接口1: java.util.Map
接口2: java.lang.Cloneable
接口3: java.io.Serializable

=== 案例2:反射创建对象 ===
unknownName 0
lisi 19

=== 案例3:操作对象属性 ===
public java.lang.String name
zhangsan 0

=== 案例4:调用方法 ===
lisi 20

=== 案例5:数组反射操作 ===
数组的类型:int
数组的长度:3
数组的第一个元素:1
1 2 3 4 

=== 案例6:注解反射 ===
Demo类是否为注解类:false
main方法是否有Target注解:false

8. 反射的高级应用

8.1 私有字段和方法的访问

public class PrivateAccessDemo {
    public static void main(String[] args) throws Exception {
        Person person = new Person();
        Class<?> clazz = person.getClass();
        
        // 访问私有字段
        Field ageField = clazz.getDeclaredField("age");
        ageField.setAccessible(true);  // 关键:设置可访问
        ageField.set(person, 25);
        
        System.out.println("私有字段age的值:" + ageField.get(person));
        
        // 如果有私有方法,也可以这样访问
        // Method privateMethod = clazz.getDeclaredMethod("privateMethodName");
        // privateMethod.setAccessible(true);
        // privateMethod.invoke(person);
    }
}

8.2 动态代理示例

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 接口
interface UserService {
    void save(String user);
    String find(String id);
}

// 实现类
class UserServiceImpl implements UserService {
    @Override
    public void save(String user) {
        System.out.println("保存用户:" + user);
    }
    
    @Override
    public String find(String id) {
        return "用户" + id;
    }
}

// 动态代理处理器
class LogInvocationHandler implements InvocationHandler {
    private Object target;
    
    public LogInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法调用前:" + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("方法调用后:" + method.getName());
        return result;
    }
}

// 使用示例
public class ProxyDemo {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new LogInvocationHandler(target)
        );
        
        proxy.save("张三");
        String user = proxy.find("001");
        System.out.println(user);
    }
}

8.3 反射工具类

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class ReflectionUtils {
    
    /**
     * 获取类的所有字段(包括父类)
     */
    public static List<Field> getAllFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        while (clazz != null) {
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field : declaredFields) {
                fields.add(field);
            }
            clazz = clazz.getSuperclass();
        }
        return fields;
    }
    
    /**
     * 获取对象的字段值
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            throw new RuntimeException("获取字段值失败", e);
        }
    }
    
    /**
     * 设置对象的字段值
     */
    public static void setFieldValue(Object obj, String fieldName, Object value) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        } catch (Exception e) {
            throw new RuntimeException("设置字段值失败", e);
        }
    }
    
    /**
     * 调用方法
     */
    public static Object invokeMethod(Object obj, String methodName, Object... args) {
        try {
            Class<?>[] paramTypes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                paramTypes[i] = args[i].getClass();
            }
            
            Method method = obj.getClass().getMethod(methodName, paramTypes);
            return method.invoke(obj, args);
        } catch (Exception e) {
            throw new RuntimeException("方法调用失败", e);
        }
    }
    
    /**
     * 创建实例
     */
    @SuppressWarnings("unchecked")
    public static <T> T createInstance(Class<T> clazz) {
        try {
            return clazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("创建实例失败", e);
        }
    }
}

9. 反射的优缺点

9.1 优点

  1. 运行时获取类信息:可以在运行时检查类的结构
  2. 动态创建对象:不需要在编译时确定类型
  3. 调用任意方法:包括私有方法
  4. 访问任意字段:包括私有字段
  5. 框架开发基础:Spring、Hibernate等框架的核心技术

9.2 缺点

  1. 性能开销:反射操作比直接调用慢
  2. 安全限制:可能违反封装性
  3. 代码复杂性:增加代码的复杂度和维护难度
  4. 编译时检查缺失:失去编译时的类型检查

9.3 使用场景

场景说明
框架开发Spring IoC、ORM框架等
配置文件解析根据配置动态创建对象
插件系统动态加载和使用插件
序列化/反序列化JSON、XML等数据转换
单元测试测试私有方法和字段
代码生成工具自动生成代码

10. 常见异常

异常类型及处理

异常名称异常描述常见原因
ClassNotFoundException未找到类异常类名错误或类路径问题
InstantiationException无法实例化异常抽象类或接口无法实例化
IllegalAccessException非法存取异常访问权限不足
SecurityException安全管理器异常安全策略限制
NoSuchMethodException方法未找到异常方法名或参数类型错误
IllegalArgumentException不合法参数异常参数类型或数量不匹配
InvocationTargetException调用目标异常被调用方法内部抛出异常
NoSuchFieldException字段未找到异常字段名错误

异常处理示例

public class ExceptionHandlingDemo {
    public static void main(String[] args) {
        try {
            // 可能出现ClassNotFoundException
            Class<?> clazz = Class.forName("com.example.NonExistentClass");
            
            // 可能出现InstantiationException
            Object instance = clazz.newInstance();
            
            // 可能出现NoSuchFieldException
            Field field = clazz.getDeclaredField("nonExistentField");
            
            // 可能出现IllegalAccessException
            field.get(instance);
            
        } catch (ClassNotFoundException e) {
            System.err.println("类未找到:" + e.getMessage());
        } catch (InstantiationException e) {
            System.err.println("实例化失败:" + e.getMessage());
        } catch (IllegalAccessException e) {
            System.err.println("访问权限不足:" + e.getMessage());
        } catch (NoSuchFieldException e) {
            System.err.println("字段未找到:" + e.getMessage());
        } catch (Exception e) {
            System.err.println("其他异常:" + e.getMessage());
            e.printStackTrace();
        }
    }
}

11. 反射性能优化

11.1 缓存反射对象

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;

public class ReflectionCache {
    private static final Map<String, Method> methodCache = new ConcurrentHashMap<>();
    
    public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
        String key = clazz.getName() + "#" + methodName;
        
        return methodCache.computeIfAbsent(key, k -> {
            try {
                return clazz.getMethod(methodName, paramTypes);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("方法不存在:" + methodName, e);
            }
        });
    }
}

11.2 使用MethodHandle(Java 7+)

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MethodHandleDemo {
    public static void main(String[] args) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        
        // 比反射更快的方法调用
        MethodHandle handle = lookup.findVirtual(
            String.class, 
            "substring", 
            MethodType.methodType(String.class, int.class)
        );
        
        String result = (String) handle.invoke("Hello World", 6);
        System.out.println(result); // World
    }
}

12. 总结

反射的核心价值

  1. 动态性:程序可以在运行时获取和操作类信息
  2. 灵活性:不需要在编译时确定具体的类和方法
  3. 通用性:可以编写处理任意类型的通用代码
  4. 框架基础:是许多Java框架的核心技术

最佳实践

  1. 谨慎使用:只在确实需要动态性时使用反射
  2. 性能考虑:缓存反射获取的对象,避免重复获取
  3. 异常处理:妥善处理反射可能抛出的异常
  4. 安全性:使用setAccessible()时要考虑安全性
  5. 可维护性:反射代码要有充分的注释和文档

适用场景建议

推荐使用谨慎使用不推荐使用
框架开发业务逻辑中偶尔使用频繁的性能敏感操作
配置驱动的系统工具类开发简单的对象创建
插件系统测试代码已知类型的操作
序列化框架代码生成大量重复调用

反射是Java中强大而危险的特性,正确理解和使用反射,能够让我们编写出更加灵活和通用的代码,但也要注意其带来的性能和安全性问题。


本文全面介绍了Java反射机制的原理、核心类、实际应用和注意事项,希望对深入理解和掌握Java反射技术有所帮助。