Skip to content

Java 反射 (Reflection)

反射(Reflection)是 Java 提供的一种强大的高级特性,它允许正在运行的 Java 程序检查或“反思”自身,并能在运行时操作类、接口、字段和方法。

什么是反射?

反射机制允许程序在运行时:

  • 获取任意一个类的完整结构(包括它的父类、接口、构造方法、方法、字段等)。
  • 在运行时创建任意一个类的实例。
  • 在运行时调用任意一个对象的方法。
  • 在运行时获取或设置任意一个对象的字段值,即使是 private 成员。

反射的核心是 java.lang.Class 类和 java.lang.reflect 包中的一系列类,如 Constructor, Method, Field 等。

Class 对象

每个加载到 JVM 中的类,都会在内存中创建一个对应的 Class 对象。这个 Class 对象包含了该类的所有信息,是反射的入口点。

获取 Class 对象的三种方式

  1. 通过类名 .class:最直接、最安全的方式,在编译时就会受到检查。

    java
    Class<String> stringClass = String.class;
  2. 通过对象的 getClass() 方法:如果已经有了一个对象实例,可以调用它的 getClass() 方法。

    java
    String s = "Hello";
    Class<?> sClass = s.getClass();
  3. 通过类的完全限定名 Class.forName():这种方式最灵活,可以在运行时根据一个字符串动态加载类。

    java
    try {
        Class<?> driverClass = Class.forName("com.mysql.cj.jdbc.Driver");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

使用反射检查类信息

一旦获得了 Class 对象,就可以用它来探索类的内部结构。

java
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionInfoExample {
    public static void main(String[] args) {
        Class<String> clazz = String.class;

        // 获取类名
        System.out.println("类名: " + clazz.getName());
        System.out.println("简单类名: " + clazz.getSimpleName());

        // 获取所有 public 字段
        System.out.println("\nPublic Fields:");
        for (Field field : clazz.getFields()) {
            System.out.println(" - " + field.getName());
        }

        // 获取所有声明的字段(包括 private)
        System.out.println("\nDeclared Fields:");
        for (Field field : clazz.getDeclaredFields()) {
            System.out.println(" - " + field.getName());
        }

        // 获取所有 public 方法
        System.out.println("\nPublic Methods:");
        for (Method method : clazz.getMethods()) {
            System.out.println(" - " + method.getName());
        }
    }
}

使用反射创建实例和调用方法

反射最强大的功能是在运行时动态地创建对象和执行操作。

创建实例

  • 如果类有公共的无参构造方法,可以直接调用 clazz.newInstance() (已过时) 或 clazz.getDeclaredConstructor().newInstance()
  • 如果需要使用带参数的构造方法,需要先获取 Constructor 对象。

调用方法

  • 通过 getMethod()getDeclaredMethod() 获取 Method 对象。
  • 调用 method.invoke(object, args...) 来执行方法。
java
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectionActionExample {
    public static void main(String[] args) throws Exception {
        // 1. 获取 Class 对象
        Class<?> clazz = String.class;

        // 2. 创建实例
        // 获取 String(char[]) 构造方法
        Constructor<?> constructor = clazz.getConstructor(char[].class);
        char[] value = {'H', 'e', 'l', 'l', 'o'}; 
        Object instance = constructor.newInstance(value);
        System.out.println("创建的实例: " + instance); // "Hello"

        // 3. 调用方法
        // 获取 toUpperCase() 方法
        Method method = clazz.getMethod("toUpperCase");
        // 调用方法,对于实例方法,第一个参数是实例对象
        Object result = method.invoke(instance);
        System.out.println("调用 toUpperCase() 结果: " + result); // "HELLO"
    }
}

访问私有成员

反射甚至可以突破 private 访问限制。这在单元测试或某些框架中很有用,但应谨慎使用,因为它破坏了封装性。

  • 使用 getDeclaredField()getDeclaredMethod() 获取私有成员。
  • 在访问前,必须调用 setAccessible(true) 来禁用访问安全检查。
java
// 假设有一个 Person 类
// public class Person {
//     private String name;
//     public Person(String name) { this.name = name; }
// }

// Person person = new Person("Alice");
// Field nameField = person.getClass().getDeclaredField("name");
// nameField.setAccessible(true); // 禁用访问检查
// String nameValue = (String) nameField.get(person);
// System.out.println(nameValue); // 输出 "Alice"

反射的优缺点

优点:

  • 灵活性和动态性:使程序能够在运行时加载、检查和使用编译时未知的类。
  • 框架开发:是许多现代框架(如 Spring, Hibernate, JUnit)的基石,用于实现依赖注入、ORM 等功能。

缺点:

  • 性能开销:反射操作比直接代码调用要慢得多。
  • 安全性问题:可以绕过访问控制,破坏封装性。
  • 代码可读性差:反射代码通常更复杂,更难理解和调试。

本站内容仅供学习和研究使用。