Java反射机制

反射

  • 类的反射机制(Reflection)是 Java 提供的一种强大的功能,允许程序在运行时动态地获取类的信息并操作类的属性、方法和构造器。通过反射,程序可以在运行时分析类、接口、字段和方法,甚至可以调用方法、构造对象、修改字段值等

  • 反射机制的核心类是 java.lang.Class,它是反射的入口点。通过 Class 对象,可以获取类的所有信息

  • Class类也是类,继承Obejct

  • 一个类的Class对象内存中存在一份,由系统创建,有加锁机制

  • Class对象存储在堆中,类的元数据类名、父类名、接口列表、字段信息、方法信息、常量池等存放在方法区中

反射的主要功能

  1. 获取类的信息
    • 获取类的名称、修饰符、父类、接口、注解等
  2. 操作类的字段
    • 获取类的字段(成员变量),包括字段的名称、类型、修饰符等。
    • 动态读取或修改字段的值
  3. 调用类的方法
    • 获取类的方法,包括方法的名称、参数类型、返回类型、修饰符等
    • 动态调用方法
  4. 操作类的构造器
    • 获取类的构造器,包括构造器的参数类型、修饰符等。
    • 动态创建对象
  5. 动态代理
    • 通过反射实现动态代理,用于 AOP(面向切面编程)等场景

反射的核心类

  1. java.lang.Class
    • 表示一个类或接口的元数据。
    • 通过 Class 对象可以获取类的所有信息
  2. java.lang.reflect.Field
    • 表示类的字段(成员变量)。
    • 可以获取或修改字段的值
  3. java.lang.reflect.Method
    • 表示类的方法。
    • 可以调用方法
  4. java.lang.reflect.Constructor
    • 表示类的构造器。
    • 可以创建对象

反射的使用

获取class对象

Class 对象是反射的入口,获取 Class 对象的方式有以下几种

  • Class.forName("全限定类名")

    Class<?> clazz = Class.forName("com.example.Person");
    
  • 类名.class

    Class<?> clazz = Person.class;
    
  • 对象.getClass()

    Person person = new Person();
    Class<?> clazz = person.getClass();
    

获取类的相关信息

通过 Class 对象可以获取类的名称、修饰符、父类、接口等信息

Class<?> clazz = Person.class;

// 获取类名
String className = clazz.getName(); // 全限定类名
String simpleName = clazz.getSimpleName(); // 简单类名

// 获取修饰符
int modifiers = clazz.getModifiers(); // 返回修饰符的整型值
String modifierStr = Modifier.toString(modifiers); // 转换为字符串

// 获取父类
Class<?> superClass = clazz.getSuperclass();

// 获取实现的接口
Class<?>[] interfaces = clazz.getInterfaces();

操作字段

通过 Class 对象可以获取类的字段,并动态读取或修改字段的值

Class<?> clazz = Person.class;

// 获取公共字段(包括从父类继承的字段)
Field[] publicFields = clazz.getFields();
// 返回类中声明的所有字段(包括私有字段,但不包括从父类继承的字段)
Field[] fields = clazz.getDeclaredFields();

// 获取指定字段
Field nameField = clazz.getDeclaredField("name");

// 如果字段是私有的,需要先调用 setAccessible(true) 来绕过访问控制检查
nameField.setAccessible(true);

// 获取字段值
Person person = new Person();
Object value = nameField.get(person); // 获取 person 对象的 name 字段值

// 修改字段值
nameField.set(person, "Alice"); // 将 person 对象的 name 字段值设置为 "Alice"

可以遍历得到的fields数组来得到每个fields的具体信息,如字段的具体名称与类型、修饰符

// 获取字段名称
String fieldName = field.getName();

// 获取字段类型
Class<?> fieldType = field.getType();

// 获取字段修饰符
//返回字段修饰符(整型值)
int modifiers = field.getModifiers();
String modifierStr = Modifier.toString(modifiers);

调用方法

通过 Class 对象可以获取类的方法,并动态调用方法

Class<?> clazz = Person.class;

// 返回类中所有 公共方法(包括从父类继承的方法)
Method[] publicMethods = clazz.getMethods();
// 返回类中声明的所有方法(包括私有方法,但不包括从父类继承的方法)
Method[] methods = clazz.getDeclaredMethods();

// 获取指定参数类型的方法
Method sayHelloMethod = clazz.getDeclaredMethod("sayHello", String.class);

// 如果方法是私有的,需要先调用 setAccessible(true) 来绕过访问控制检查
sayHelloMethod.setAccessible(true);

// 调用方法
Person person = new Person();
Object result = sayHelloMethod.invoke(person, "World"); // 调用 person 对象的 sayHello 方法

可以遍历得到的methods数组来得到每个method的具体信息,如返回值或者参数类型等

// 获取返回类型
Class<?> returnType = method.getReturnType();

// 获取参数类型
Class<?>[] parameterTypes = method.getParameterTypes();

// 获取修饰符
//返回方法的修饰符(整型值)
int modifiers = method.getModifiers();
String modifierStr = Modifier.toString(modifiers);
  • 如果字段或者方法为静态的,则可以使用null作为传入对象

    Object value = field.get(null); // 获取静态字段的值
    field.set(null, "Bob"); // 修改为 Bob
    method.invoke(null); // 调用无参方法
    method.invoke(null, "Alice"); // 调用带参方法
    

操作构造器

通过 Class 对象可以获取类的构造器,并动态创建对象

Class<?> clazz = Person.class;

// 获取所有构造器
Constructor<?>[] constructors = clazz.getDeclaredConstructors();

// 获取指定构造器
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);

// 如果构造器是私有的,需要先调用 setAccessible(true) 来绕过访问控制检查
constructor.setAccessible(true);

// 创建对象
Person person = (Person) constructor.newInstance("Alice");

可以遍历得到的constructors数组来得到每个constructor的具体信息,如构造器名称或者参数类型等

// 获取构造器名称
String constructorName = constructor.getName();

// 获取参数类型
Class<?>[] parameterTypes = constructor.getParameterTypes();

// 获取修饰符
int modifiers = constructor.getModifiers();
String modifierStr = Modifier.toString(modifiers);

反射的优缺点

  • 优点
    • 动态性:反射允许程序在运行时动态地加载类、调用方法、操作字段,提高了程序的灵活性。
    • 通用性:反射可以编写通用的工具类,例如 JSON 序列化/反序列化工具、ORM 框架等。
    • 扩展性:反射可以加载外部类或插件,实现程序的扩展
  • 缺点
    • 性能开销:反射操作比直接调用方法或访问字段慢,因为 JVM 无法优化反射调用。
    • 安全性问题:反射可以访问私有字段和方法,破坏了封装性,可能导致安全问题。
    • 代码可读性差:反射代码通常比较复杂,可读性和可维护性较差。
posted @ 2025-03-17 21:04  QAQ001  阅读(66)  评论(0)    收藏  举报