Java-反射

Java-反射

_
本文内容由 AI 辅助生成。

一、什么是反射?

反射(Reflection) 是 Java 提供的一种在运行时动态获取类信息并操作对象的机制。

📌 核心能力

  • 在运行时获取任意类的 Class 对象;

  • 动态创建对象实例;

  • 获取/调用类的字段(Field)、方法(Method)、构造器(Constructor);

  • 突破访问控制(如访问私有成员);

  • 分析泛型、注解等元数据。

本质
Java 将“类”也视为一种对象(java.lang.Class),通过它可以在运行时“自省”(Introspection)和“操控”程序结构。

⚠️ 注意:反射是“双刃剑”——强大但有代价(性能、安全、可维护性)。


二、反射的核心 API(位于 java.lang.reflect 包)

作用

Class<?>

表示类的元数据(入口)

Field

表示类的成员变量(字段)

Method

表示类的方法

Constructor<T>

表示类的构造器

Modifier

解析访问修饰符(如 public, private


三、获取 Class 对象的三种方式

// 1. 通过对象的 getClass() 方法(运行时)
String str = "Hello";
Class<?> clazz1 = str.getClass();

// 2. 通过类字面量(编译期已知,推荐!)
Class<String> clazz2 = String.class;

// 3. 通过 Class.forName()(常用于动态加载,需处理异常)
try {
    Class<?> clazz3 = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

最佳实践:优先使用 String.class,避免异常,性能更好。


四、反射的典型用法

1. 创建对象实例

public class Person {
    private String name;
    private int age;

    // 私有构造器
    private Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

// 使用反射创建对象
public class ReflectionExample {
    public static void main(String[] args) throws Exception {
        Class<Person> clazz = Person.class;

        // 获取私有构造器(参数类型为 String 和 int)
        Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class, int.class);

        // 设置可访问(突破 private 限制)
        constructor.setAccessible(true);

        // 创建实例
        Person person = constructor.newInstance("张三", 25);
        System.out.println(person); // Person{name='张三', age=25}
    }
}

2. 操作私有字段(Field)

// 修改私有字段
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 允许访问私有成员

// 为 person 对象的 name 字段赋值
nameField.set(person, "李四");

// 读取字段值
String newName = (String) nameField.get(person);
System.out.println(newName); // 李四

3. 调用私有方法(Method)

// 在 Person 类中添加一个私有方法
class Person {
    // ... 上文字段和构造器 ...

    private void sayHello(String message) {
        System.out.println(name + " says: " + message);
    }
}

// 反射调用
Method sayHelloMethod = clazz.getDeclaredMethod("say_hello", String.class); // 注意方法名拼写
sayHelloMethod.setAccessible(true);
sayHelloMethod.invoke(person, "你好,反射!");
// 输出: 李四 says: 你好,反射!

🔍 注意:getDeclaredMethod 只获取当前类声明的方法,不包括父类;若需父类方法,用 getMethod()(但只能获取 public 方法)。


4. 获取类的完整信息

Class<?> clazz = ArrayList.class;

// 获取类名
System.out.println("类名: " + clazz.getName()); // java.util.ArrayList

// 获取所有 public 方法(包括继承的)
Method[] publicMethods = clazz.getMethods();
for (Method m : publicMethods) {
    System.out.println("Public Method: " + m.getName());
}

// 获取本类声明的所有字段(包括 private)
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
    System.out.println("Declared Field: " + f.getName());
}

// 获取实现的接口
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> itf : interfaces) {
    System.out.println("Implements: " + itf.getName());
}

// 获取父类
Class<?> superclass = clazz.getSuperclass();
System.out.println("Superclass: " + superclass.getName());

5. 操作泛型与注解(高级用法)

示例:获取字段的泛型类型

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

class Student {
    private List<String> courses; // 泛型字段
}

// 获取泛型信息
Field field = Student.class.getDeclaredField("courses");
Type genericType = field.getGenericType();

if (genericType instanceof ParameterizedType) {
    ParameterizedType pt = (ParameterizedType) genericType;
    Type[] actualTypes = pt.getActualTypeArguments();
    System.out.println("泛型类型: " + actualTypes[0]); // class java.lang.String
}

示例:读取注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Column {
    String name();
}

class User {
    @Column(name = "user_name")
    private String username;
}

// 读取注解
Field field = User.class.getDeclaredField("username");
if (field.isAnnotationPresent(Column.class)) {
    Column col = field.getAnnotation(Column.class);
    System.out.println("数据库列名: " + col.name()); // user_name
}

✅ 注解必须设置 @Retention(RetentionPolicy.RUNTIME) 才能在运行时通过反射获取。


五、反射的扩展应用场景

1. 框架底层实现

  • Spring:通过反射创建 Bean、注入依赖、处理 @Autowired

  • Hibernate / MyBatis:映射数据库字段到 Java 对象(ORM)。

  • JUnit:通过反射查找并执行 @Test 方法。

  • Gson / Jackson:通过反射序列化/反序列化 JSON。

2. 动态代理基础

  • java.lang.reflect.Proxy 基于反射实现接口代理。

  • AOP(面向切面编程)常用此机制。

3. 插件系统 / 热加载

  • 动态加载 JAR 包中的类并实例化(如 Class.forName("com.xxx.Plugin"))。

4. 通用工具类

  • BeanUtils.copyProperties()(Apache Commons)利用反射复制对象属性。


六、反射的缺点与注意事项

问题

说明

性能开销大

反射涉及动态解析,比直接调用慢 10~100 倍(可通过缓存 Method/Field 缓解)

破坏封装性

可访问私有成员,违背面向对象设计原则

安全性风险

可能被恶意代码利用(可通过 SecurityManager 限制)

编译期无检查

错误(如方法名拼错)只能在运行时暴露

混淆/优化影响

ProGuard 等工具可能移除未显式引用的类,导致 ClassNotFoundException

建议

  • 仅在必要时使用反射(如框架开发);

  • 避免在高频路径(如循环)中使用;

  • 缓存 MethodField 等反射对象;

  • 优先使用标准 API(如 MapFunction)替代反射。


七、反射 vs 其他动态机制

机制

特点

适用场景

反射(Reflection)

功能最全,可操作任意成员

通用动态操作

方法句柄(MethodHandle)

JDK 7 引入,更接近 JVM 指令,性能优于反射

高性能动态调用(如 invokedynamic)

动态代理(Proxy)

仅支持接口代理

AOP、RPC 代理

字节码生成(ASM, ByteBuddy)

运行时生成新类

高性能 ORM、Mock 框架


八、总结

维度

内容

核心价值

实现“运行时动态编程”,支撑现代 Java 框架

关键类

Class, Field, Method, Constructor

三大能力

获取信息、创建对象、调用成员

两大风险

性能损耗、破坏封装

最佳实践

缓存反射对象、谨慎突破访问控制、优先使用注解+反射组合

财务概念释义-借贷记账法 2026-01-07
Java 设计模式:单例模式(Singleton Pattern) 2026-01-07

评论区

© 2026 何歡囍