什么是反射?
平时使用 Java 类,通常是先知道类名,通过 new 关键字创建对象,再调用方法或属性。但如果运行时只知道类的名字符串呢?比如配置文件里动态指定的类名。这时候就需要用到反射了。
简单来说,反射机制允许在运行状态中,对任意一个类都能获取它的所有属性和方法;对任意一个对象,都能调用它的任意方法和属性。这种动态获取信息以及动态调用对象的方法的功能,就是 Java 语言的反射机制。
核心 API 概览
反射相关的类主要位于 java.lang.reflect 包中(除了 Class)。它们将类的类型、方法、属性都封装成了独立的类:
- Class:反射的基石,代表加载到内存中的类。
- Constructor:构造方法。
- Method:普通方法。
- Field:成员变量。
其中最重要的类是 Class,可以说反射的使用都是从获取 Class 实例开始的。
获取 Class 实例
要使用反射,必须先拿到 Class 对象的实例。常用的方式有三种:
- 对象.getClass():适用于已有对象的情况。任何对象都可以调用此方法获得对应的 Class 实例。
- 类名.class:适用于编译期已知类名的情况。例如
String.class直接返回 String 类的 Class 实例。 - Class.forName(String name):适用于动态场景,尤其当类名是变量时。这是框架开发中最常用的方式。
// 示例:通过类名字符串获取 Class 实例
String className = "java.util.ArrayList";
Class<?> clazz = Class.forName(className);
操作构造器 (Constructor)
Constructor 类封装了构造方法的信息。拿到 Constructor 实例后,不仅能查看构造方法的参数和名称,还能直接创建新的对象实例。
// 获取带参数的构造方法
Constructor<?> constructor = clazz.getConstructor(String.class);
// 创建实例
Object obj = constructor.newInstance("初始化参数");
注意权限问题,如果构造方法是私有的,可能需要特殊处理才能访问。
操作字段 (Field)
Field 类将类的属性进行封装,可以获取属性的基本信息、当前值,也可以修改属性值。这对于操作私有字段非常有用。
// 获取声明的字段(包括私有)
Field field = clazz.getDeclaredField("privateVar");
// 暴力反射,绕过访问控制检查
field.setAccessible(true);
// 设置值
field.set(obj, "新值");
// 获取值
Object value = field.get(obj);
在实际使用中,setAccessible(true) 是关键,它能让我们访问原本不可见的私有成员。
操作方法 (Method)
Method 类封装了类中的方法。除了动态获取方法签名(如参数类型、返回值),Method 最强大的功能是动态调用某个对象的具体方法。
// 获取公开方法
Method method = clazz.getMethod("methodName", String.class);
// 调用方法,第一个参数是目标对象,后续是方法参数
Object result = method.invoke(obj, "参数");
这里要注意,invoke 的第一个参数必须是该方法所属的对象实例(如果是静态方法则为 null)。
总结
反射让程序具备了极高的灵活性,是许多框架(如 Spring、MyBatis)的核心基础。但同时也带来了性能损耗和安全风险,使用时需谨慎权衡。


