12、Java 反射机制详解

【投稿赢 iPhone 17】「我的第一个开源项目」故事征集:用代码换C位出道! 10w+人浏览 1.6k人参与

开篇概览:反射的核心价值

反射(Reflection) 是 Java 在运行时动态分析、操作类与对象的能力。它允许程序在运行时:

  • 获取类的完整结构(字段、方法、构造器等);
  • 动态创建对象;
  • 动态调用方法、访问字段;
  • 突破访问控制(如访问 private 成员)。

反射是许多高级框架(如 Spring、Hibernate、JUnit)的底层基础,也是实现插件化、动态代理、ORM 映射等技术的关键。

⚠️ 重要前提

  • 反射操作性能较低(比直接调用慢数倍);
  • 破坏封装性,应谨慎使用;
  • 仅在编译期无法确定类型时使用(如框架开发、通用工具)。

一、Class 类:反射的入口

java.lang.Class 是反射的核心类,代表运行时的类元数据。每个类在 JVM 中有且仅有一个 Class 对象。

1.1 获取 Class 对象的三种方式

方式说明示例
类名.class编译期已知类型String.class
对象.getClass()通过实例获取"hello".getClass()
Class.forName("全限定类名")动态加载(常用)Class.forName("java.lang.String")

1.2 示例:获取 Class 对象

public class ClassDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式1:通过类字面量(推荐,简洁安全)
        Class<String> stringClass1 = String.class;

        // 方式2:通过对象实例
        String str = "Hello";
        Class<? extends String> stringClass2 = str.getClass();

        // 方式3:通过全限定类名(动态加载,可能抛出 ClassNotFoundException)
        Class<?> stringClass3 = Class.forName("java.lang.String");

        // 验证三个 Class 对象是否相同(同一类在 JVM 中只有一个 Class 实例)
        System.out.println("方式1 == 方式2: " + (stringClass1 == stringClass2)); // true
        System.out.println("方式1 == 方式3: " + (stringClass1 == stringClass3)); // true

        // 获取类的基本信息
        System.out.println("类名(含包): " + stringClass1.getName());       // java.lang.String
        System.out.println("简单类名: " + stringClass1.getSimpleName());     // String
        System.out.println("是否为接口: " + stringClass1.isInterface());    // false
        System.out.println("是否为数组: " + stringClass1.isArray());        // false
    }
}

关键点

  • Class.forName()初始化类(执行 static 块);
  • 若只需加载类而不初始化,使用 Class.forName(name, false, loader)

二、获取类的成员信息

通过 Class 对象可获取类的构造器、字段、方法等元数据。

2.1 核心方法概览

获取内容方法返回类型
构造器getConstructors() / getDeclaredConstructors()Constructor<?>[]
字段getFields() / getDeclaredFields()Field[]
方法getMethods() / getDeclaredMethods()Method[]

🔑 getXXX() vs getDeclaredXXX()

  • getXXX():仅返回 public 成员(包括继承的);
  • getDeclaredXXX():返回 所有声明的成员(含 private/protected,但不包括继承的)。

2.2 示例:分析类的结构(详细中文注释)

import java.lang.reflect.*;

// 定义一个测试类(含 private 成员)
class Person {
    public String name;
    private int age;
    protected String email;

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

    public void sayHello() {
        System.out.println("Hello, I'm " + name);
    }

    private void setAge(int age) {
        this.age = age;
    }
}

public class ReflectionAnalysisDemo {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;

        // 1. 获取所有 public 构造器(包括继承的,但 Object 无 public 构造器)
        System.out.println("=== public 构造器 ===");
        Constructor<?>[] publicConstructors = personClass.getConstructors();
        for (Constructor<?> c : publicConstructors) {
            System.out.println("构造器: " + c);
        }
        // 输出: public Person()

        // 2. 获取所有声明的构造器(含 private)
        System.out.println("\n=== 所有声明的构造器 ===");
        Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
        for (Constructor<?> c : declaredConstructors) {
            System.out.println("构造器: " + c);
        }
        // 输出: 
        // public Person()
        // private Person(java.lang.String,int)

        // 3. 获取所有 public 字段(包括继承的)
        System.out.println("\n=== public 字段 ===");
        Field[] publicFields = personClass.getFields();
        for (Field f : publicFields) {
            System.out.println("字段: " + f.getType().getSimpleName() + " " + f.getName());
        }
        // 输出: String name

        // 4. 获取所有声明的字段(含 private/protected)
        System.out.println("\n=== 所有声明的字段 ===");
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field f : declaredFields) {
            System.out.println("字段: " + f.getType().getSimpleName() + " " + f.getName() + 
                             " (修饰符: " + Modifier.toString(f.getModifiers()) + ")");
        }
        // 输出:
        // String name (修饰符: public)
        // int age (修饰符: private)
        // String email (修饰符: protected)

        // 5. 获取所有 public 方法(包括继承的 Object 方法)
        System.out.println("\n=== public 方法 ===");
        Method[] publicMethods = personClass.getMethods();
        for (Method m : publicMethods) {
            System.out.println("方法: " + m.getName());
        }
        // 输出: sayHello, wait, equals, toString, hashCode 等

        // 6. 获取所有声明的方法(不含继承的)
        System.out.println("\n=== 所有声明的方法 ===");
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method m : declaredMethods) {
            System.out.println("方法: " + m.getName() + 
                             " (修饰符: " + Modifier.toString(m.getModifiers()) + ")");
        }
        // 输出:
        // sayHello (修饰符: public)
        // setAge (修饰符: private)
    }
}

关键点

  • 使用 Modifier.toString() 可读化修饰符;
  • getMethods() 包含从 Object 继承的方法(如 toString)。

三、通过反射操作对象

3.1 创建对象

方式1:调用无参构造器
// 使用 Class.newInstance()(已废弃,Java 9+)
// Person p = (Person) personClass.newInstance();

// 推荐方式:通过 Constructor
Constructor<Person> constructor = personClass.getConstructor();
Person person = constructor.newInstance();
方式2:调用带参构造器(含 private)
// 获取 private 构造器
Constructor<Person> privateConstructor = personClass.getDeclaredConstructor(String.class, int.class);
// 突破访问控制
privateConstructor.setAccessible(true);
Person person2 = privateConstructor.newInstance("张三", 25);

3.2 调用方法

调用 public 方法
// 获取 public 方法
Method sayHelloMethod = personClass.getMethod("sayHello");
// 调用方法(第一个参数是对象实例)
sayHelloMethod.invoke(person2); // 输出: Hello, I'm 张三
调用 private 方法
// 获取 private 方法
Method setAgeMethod = personClass.getDeclaredMethod("setAge", int.class);
// 突破访问控制
setAgeMethod.setAccessible(true);
// 调用 private 方法
setAgeMethod.invoke(person2, 30);

3.3 访问字段

访问 public 字段
// 直接访问 public 字段
Field nameField = personClass.getField("name");
nameField.set(person2, "李四"); // 设置值
String name = (String) nameField.get(person2); // 获取值
System.out.println("姓名: " + name); // 李四
访问 private 字段
// 访问 private 字段
Field ageField = personClass.getDeclaredField("age");
ageField.setAccessible(true); // 突破访问控制
ageField.set(person2, 28);
int age = (int) ageField.get(person2);
System.out.println("年龄: " + age); // 28

四、完整示例:反射综合应用

import java.lang.reflect.*;

// 测试类
class Student {
    private String name;
    private int score;

    public Student() {}

    public void study() {
        System.out.println(name + " 正在学习,当前分数: " + score);
    }

    private void updateScore(int newScore) {
        this.score = newScore;
        System.out.println("分数已更新为: " + score);
    }
}

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

        // 2. 创建对象(调用无参构造器)
        Student student = studentClass.getConstructor().newInstance();
        System.out.println("对象创建成功: " + student);

        // 3. 设置 private 字段
        Field nameField = studentClass.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(student, "王五");

        Field scoreField = studentClass.getDeclaredField("score");
        scoreField.setAccessible(true);
        scoreField.set(student, 85);

        // 4. 调用 public 方法
        Method studyMethod = studentClass.getMethod("study");
        studyMethod.invoke(student); // 王五 正在学习,当前分数: 85

        // 5. 调用 private 方法
        Method updateScoreMethod = studentClass.getDeclaredMethod("updateScore", int.class);
        updateScoreMethod.setAccessible(true);
        updateScoreMethod.invoke(student, 92); // 分数已更新为: 92

        // 6. 验证字段值是否更新
        int currentScore = (int) scoreField.get(student);
        System.out.println("最终分数: " + currentScore); // 92
    }
}

输出

对象创建成功: reflection.Student@...
王五 正在学习,当前分数: 85
分数已更新为: 92
最终分数: 92

五、反射的局限性与最佳实践

⚠️ 局限性

  1. 性能开销大:方法调用比直接调用慢 2-3 倍;
  2. 破坏安全性:可绕过访问控制(需 SecurityManager 限制);
  3. 代码脆弱:依赖字符串(如方法名),重构易出错;
  4. 泛型擦除:运行时无法获取泛型实际类型。

✅ 最佳实践

  1. 仅在必要时使用:如框架开发、通用工具类;
  2. 缓存反射对象:避免重复获取 Method/Field
  3. 优先使用 getDeclaredXXX() + setAccessible(true)
  4. 处理异常ReflectiveOperationException(含 ClassNotFoundExceptionNoSuchMethodException 等);
  5. 考虑替代方案:如接口、Lambda、注解处理器。

六、总结:反射核心能力全景

能力方法用途
获取 Class类.class, 对象.getClass(), Class.forName()反射入口
创建对象Constructor.newInstance()动态实例化
调用方法Method.invoke()动态方法调用
访问字段Field.get() / set()动态读写属性
突破访问控制setAccessible(true)访问 private 成员

📌 核心思想
“反射是 Java 的‘自省’能力,让程序在运行时理解自身结构。”
它是一把双刃剑——用得好可构建灵活框架,用得滥则导致代码脆弱。掌握反射,是深入理解 Java 语言与 JVM 机制的关键一步。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值