Skip to content

Java 反射机制详解

什么是反射?

反射(Reflection)是 Java 语言的一个特性,它允许程序在运行时获取类的信息,并且能够动态地调用类的方法、访问类的属性。简单来说,反射就是让程序能够"看到"自己,并且能够"操作"自己。

为什么需要反射?

想象一下这样的场景:

  • 你写了一个配置文件,里面记录了要使用的类名
  • 程序运行时,根据配置文件中的类名来创建对象
  • 但是你在写代码的时候,并不知道具体要使用哪个类

这就是反射的用武之地!反射让程序能够在运行时动态地:

  • 获取类的信息(方法、属性、构造器等)
  • 创建对象
  • 调用方法
  • 访问属性

反射的核心类

Java 反射主要涉及以下几个类:

1. Class 类

Class类是反射的核心,它代表一个类的信息。

java
// 获取Class对象的三种方式
Class<?> clazz1 = String.class;                    // 通过类名.class
Class<?> clazz2 = "Hello".getClass();              // 通过对象.getClass()
Class<?> clazz3 = Class.forName("java.lang.String"); // 通过类名字符串

2. Constructor 类

代表类的构造方法。

java
// 获取构造方法
Constructor<?>[] constructors = clazz.getConstructors(); // 获取所有public构造方法
Constructor<?> constructor = clazz.getConstructor(String.class); // 获取指定参数的构造方法

// 创建对象
Object obj = constructor.newInstance("Hello");

3. Method 类

代表类的方法。

java
// 获取方法
Method[] methods = clazz.getMethods(); // 获取所有public方法
Method method = clazz.getMethod("length"); // 获取指定方法

// 调用方法
Object result = method.invoke(obj);

4. Field 类

代表类的属性。

java
// 获取属性
Field[] fields = clazz.getDeclaredFields(); // 获取所有属性(包括private)
Field field = clazz.getDeclaredField("name"); // 获取指定属性

// 访问属性
field.setAccessible(true); // 设置可访问private属性
Object value = field.get(obj); // 获取属性值
field.set(obj, "newValue"); // 设置属性值

实际应用示例

示例 1:动态创建对象

java
public class ReflectionDemo {
    public static void main(String[] args) throws Exception {
        // 通过配置文件或用户输入获取类名
        String className = "java.lang.String";

        // 动态创建对象
        Class<?> clazz = Class.forName(className);
        Constructor<?> constructor = clazz.getConstructor(String.class);
        Object obj = constructor.newInstance("Hello Reflection!");

        System.out.println(obj); // 输出: Hello Reflection!
    }
}

示例 2:动态调用方法

java
public class MethodDemo {
    public static void main(String[] args) throws Exception {
        String str = "Hello World";
        Class<?> clazz = str.getClass();

        // 获取length方法
        Method lengthMethod = clazz.getMethod("length");
        int length = (Integer) lengthMethod.invoke(str);
        System.out.println("字符串长度: " + length); // 输出: 11

        // 获取substring方法
        Method substringMethod = clazz.getMethod("substring", int.class, int.class);
        String result = (String) substringMethod.invoke(str, 0, 5);
        System.out.println("子字符串: " + result); // 输出: Hello
    }
}

示例 3:访问私有属性

java
public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
}

public class PrivateFieldDemo {
    public static void main(String[] args) throws Exception {
        Student student = new Student("张三", 20);
        Class<?> clazz = student.getClass();

        // 获取私有属性
        Field nameField = clazz.getDeclaredField("name");
        Field ageField = clazz.getDeclaredField("age");

        // 设置可访问
        nameField.setAccessible(true);
        ageField.setAccessible(true);

        // 读取属性值
        String name = (String) nameField.get(student);
        int age = (Integer) ageField.get(student);

        System.out.println("姓名: " + name); // 输出: 张三
        System.out.println("年龄: " + age);  // 输出: 20

        // 修改属性值
        nameField.set(student, "李四");
        ageField.set(student, 25);

        System.out.println("修改后姓名: " + student.getName()); // 输出: 李四
    }
}

反射的优缺点

优点

  1. 灵活性高:可以在运行时动态决定要使用的类
  2. 功能强大:可以访问私有成员,突破封装
  3. 框架基础:很多框架(如 Spring、Hibernate)都大量使用反射

缺点

  1. 性能较低:反射调用比直接调用慢
  2. 破坏封装:可以访问私有成员,可能破坏类的封装性
  3. 代码复杂:反射代码相对复杂,可读性较差

使用建议

  1. 优先使用直接调用:如果知道具体的类和方法,优先使用直接调用
  2. 合理使用:只在真正需要动态性的时候使用反射
  3. 注意性能:在性能敏感的场景下,考虑缓存反射结果
  4. 遵循封装:不要随意破坏类的封装性

总结

反射是 Java 语言的一个强大特性,它让程序具备了"自我认知"和"自我操作"的能力。虽然反射有一定的性能开销,但在需要动态性的场景下,它是不可或缺的工具。掌握反射机制,能够让你更好地理解 Java 的动态特性,也能为学习各种框架打下坚实的基础。

Released under the MIT License.