网站公告

本次更新:没啥更新的

“钱塘江上潮信起,今日方知我是我”

Skip to content

反射 ​

字数: 0 字 时长: 0 分钟

Java 反射 ​

字数: 0 字 时长: 0 分钟

1. Java 反射概述 ​

反射(Reflection)是 Java 提供的一套:

在程序运行期间动态获取类的信息,并操作类、对象、字段、方法和构造方法的机制。

正常编写 Java 代码时,我们通常在编译阶段就已经知道:

java
User user =
        new User();

user.setName(
        "kerwim"
);

user.sayHello();

这里编译器提前知道:

text
User 是什么类

setName() 是什么方法

sayHello() 是什么方法

而反射允许我们在运行时才决定:

text
操作哪个类

创建哪个对象

调用哪个方法

读取哪个字段

例如:

java
Class<?> clazz =
        Class.forName(
                "com.kerwim.User"
        );

这里类名甚至可以来自:

text
配置文件

数据库

注解

网络数据

框架扫描结果

然后程序再动态操作它。

可以简单理解:

text
普通 Java

编译期
    ↓
知道类型
    ↓
直接调用


反射

运行期
    ↓
获得 Class
    ↓
分析类结构
    ↓
动态创建 / 调用 / 修改

1.1 反射能做什么 ​

Java 反射主要可以:

text
获取类的信息

获取字段

获取方法

获取构造方法

创建对象

读取和修改字段

动态调用方法

读取注解

分析继承关系

分析接口

配合框架完成动态处理

例如:

java
Class<?> clazz =
        User.class;

可以继续获取:

java
clazz.getName();

clazz.getDeclaredFields();

clazz.getDeclaredMethods();

clazz.getDeclaredConstructors();

clazz.getAnnotations();

所以:

反射的核心入口就是 Class 对象。

1.2 为什么需要反射 ​

假设我们现在需要根据配置文件创建对象。

配置:

text
class=com.kerwim.UserService

普通写法必须提前知道:

java
UserService service =
        new UserService();

但如果配置改成:

text
class=com.kerwim.OrderService

代码也需要跟着修改。

使用反射:

java
String className =
        "com.kerwim.UserService";

Class<?> clazz =
        Class.forName(
                className
        );

Object object =
        clazz
                .getDeclaredConstructor()
                .newInstance();

只要:

text
className

发生变化,就可以创建不同对象。

因此反射特别适合:

text
框架

IOC 容器

ORM

序列化

插件系统

测试框架

注解处理

这些:

代码编写阶段无法完全确定具体类型,需要运行时动态处理的场景。

2. Class 对象 ​

理解反射之前,最重要的是理解:

java
java.lang.Class

每一个被 JVM 加载的类型,在 JVM 中都会存在对应的:

java
Class

对象。

例如:

java
User.class

对应:

text
User 类型的 Class 对象

可以简单理解:

text
User.java
    ↓
javac
    ↓
User.class 字节码
    ↓
ClassLoader 加载
    ↓
JVM
    ↓
Class<User> 对象

反射真正操作的入口并不是:

text
.java 文件

也不是直接修改:

text
.class 字节码文件

而是:

通过 JVM 中的 Class 对象获取类型的运行时信息。

2.1 一个类对应多少个 Class 对象 ​

在:

text
同一个 ClassLoader

加载范围内,同一个类型对应同一个:

java
Class

对象。

例如:

java
Class<User> class1 =
        User.class;

Class<?> class2 =
        new User().getClass();

System.out.println(
        class1 == class2
);

输出:

text
true

因为:

text
User.class

new User().getClass()

得到的是同一个运行时类型对应的 Class 对象。

不过需要注意:

同名类如果由不同 ClassLoader 加载,在 JVM 看来可以属于不同的类型,并拥有不同的 Class 对象。

这一点后面学习:

text
类加载机制

双亲委派

Tomcat

插件系统

时会更加重要。

3. 获取 Class 对象 ​

Java 中常见的获取 Class 对象方式有三种。

3.1 类名.class ​

如果编译阶段已经知道类型,可以:

java
Class<User> clazz =
        User.class;

这是最直接的方式。

例如:

java
Class<String> clazz =
        String.class;

还可以用于基本数据类型:

java
Class<Integer> intClass =
        int.class;

数组:

java
Class<String[]> arrayClass =
        String[].class;

这种方式:

text
类型明确

写法简单

没有 ClassNotFoundException

3.2 对象.getClass() ​

如果已经存在对象:

java
User user =
        new User();

可以:

java
Class<?> clazz =
        user.getClass();

getClass() 定义在:

java
Object

类中。

因为所有普通 Java 对象最终都继承:

java
Object

所以都可以:

java
object.getClass();

例如:

java
String name =
        "kerwim";

Class<?> clazz =
        name.getClass();

System.out.println(
        clazz.getName()
);

输出:

text
java.lang.String

3.3 Class.forName() ​

如果只有完整类名字符串:

java
String className =
        "com.kerwim.User";

可以:

java
Class<?> clazz =
        Class.forName(
                className
        );

这种方式在框架中非常常见。

因为类名可能来自:

text
配置文件

数据库

扫描结果

插件配置

Class.forName() 找不到类时,会抛出:

text
ClassNotFoundException

所以需要:

java
try {

    Class<?> clazz =
            Class.forName(
                    "com.kerwim.User"
            );

} catch (
        ClassNotFoundException e) {

    // 处理异常
}

默认情况下:

java
Class.forName(
        "完整类名"
)

还会触发类的初始化。

如果只希望加载而暂时不初始化,可以使用:

java
Class.forName(
        className,
        false,
        classLoader
);

3.4 三种方式对比 ​

方式使用场景是否需要已存在对象
User.class编译期已经知道类型否
user.getClass()已经存在对象是
Class.forName()运行时只有类名字符串否

可以简单记:

text
知道类型
    ↓
User.class


已有对象
    ↓
user.getClass()


只有类名
    ↓
Class.forName()

4. 获取类的基本信息 ​

拿到:

java
Class<?> clazz

以后,就可以开始分析类。

假设:

java
package com.kerwim;

public class User {
}

4.1 获取完整类名 ​

java
clazz.getName();

返回:

text
com.kerwim.User

4.2 获取简单类名 ​

java
clazz.getSimpleName();

返回:

text
User

4.3 获取包信息 ​

java
clazz.getPackageName();

返回:

text
com.kerwim

4.4 获取父类 ​

java
Class<?> superclass =
        clazz.getSuperclass();

例如:

text
User extends Person

那么:

java
clazz.getSuperclass();

可以得到:

text
Person

对应的 Class 对象。

4.5 获取接口 ​

java
Class<?>[] interfaces =
        clazz.getInterfaces();

例如:

java
public class User
        implements Serializable,
                   Comparable<User> {
}

就可以获得对应接口。

4.6 判断类型 ​

常见方法:

java
clazz.isInterface();

clazz.isEnum();

clazz.isAnnotation();

clazz.isArray();

clazz.isPrimitive();

clazz.isRecord();

例如:

java
System.out.println(
        String.class
                .isInterface()
);

输出:

text
false

5. 获取字段 Field ​

反射中:

java
java.lang.reflect.Field

用于描述:

text
字段

假设:

java
public class User {

    public Long id;

    private String name;

    private int age;
}

5.1 getFields() ​

java
Field[] fields =
        clazz.getFields();

它主要获取:

当前类以及父类中可访问的 public 字段。

例如:

java
for (Field field : fields) {

    System.out.println(
            field.getName()
    );
}

5.2 getDeclaredFields() ​

java
Field[] fields =
        clazz.getDeclaredFields();

它获取:

当前类自己声明的所有字段。

包括:

text
public

protected

默认权限

private

但它:

text
不会自动包含父类声明的字段

所以:

text
getFields()
    ↓
public
    ↓
包含继承来的


getDeclaredFields()
    ↓
当前类声明的全部字段
    ↓
不自动向父类查找

5.3 获取指定字段 ​

例如:

java
Field field =
        clazz.getDeclaredField(
                "name"
        );

如果找不到:

text
name

会抛出:

text
NoSuchFieldException

5.4 获取字段信息 ​

拿到:

java
Field field

以后可以:

java
field.getName();

field.getType();

field.getModifiers();

field.getAnnotations();

例如:

java
System.out.println(
        field.getName()
);

System.out.println(
        field.getType()
);

可能输出:

text
name

class java.lang.String

6. 通过反射操作字段 ​

反射不仅能:

text
看字段

还可以:

text
读取字段值

修改字段值

例如:

java
public class User {

    public String name;
}

创建对象:

java
User user =
        new User();

获得字段:

java
Field field =
        User.class
                .getField(
                        "name"
                );

设置:

java
field.set(
        user,
        "kerwim"
);

读取:

java
Object value =
        field.get(user);

System.out.println(value);

输出:

text
kerwim

这里:

java
field.set(
        user,
        "kerwim"
);

可以理解:

text
user.name = "kerwim"

而:

java
field.get(user)

相当于:

text
读取 user.name

6.1 操作 private 字段 ​

如果字段是:

java
private String name;

直接:

java
field.get(user);

通常会受到 Java 访问控制限制。

传统反射代码中经常:

java
Field field =
        User.class
                .getDeclaredField(
                        "name"
                );

field.setAccessible(
        true
);

field.set(
        user,
        "kerwim"
);

然后:

java
Object value =
        field.get(user);

这样可以绕过普通 Java 语言级别的访问检查。

但现代 Java 需要注意:

setAccessible(true) 并不意味着任何 private 成员都一定可以无限制访问。

Java 9 引入模块系统以后,强封装可能阻止对其他模块内部成员的深层反射。

某些情况下可能出现:

text
InaccessibleObjectException

所以:

text
setAccessible(true)

应该理解为:

text
请求取消普通访问检查

而不是:

text
拥有无限权限

7. 获取方法 Method ​

反射中的:

java
java.lang.reflect.Method

用于描述:

text
方法

假设:

java
public class User {

    public void sayHello() {

        System.out.println(
                "hello"
        );
    }

    private String getName(
            String prefix) {

        return prefix
                + "kerwim";
    }
}

7.1 getMethods() ​

java
Method[] methods =
        clazz.getMethods();

主要获取:

text
当前类以及父类 / 接口中的 public 方法

包括:

java
Object

中继承来的:

text
toString()

hashCode()

getClass()

equals()

等方法。

7.2 getDeclaredMethods() ​

java
Method[] methods =
        clazz.getDeclaredMethods();

获取:

当前类自己声明的全部方法。

包括:

text
public

protected

默认权限

private

但不会自动遍历父类方法。

7.3 获取指定方法 ​

无参:

java
Method method =
        clazz.getDeclaredMethod(
                "sayHello"
        );

有参数:

java
Method method =
        clazz.getDeclaredMethod(
                "getName",
                String.class
        );

为什么需要传:

java
String.class

?

因为 Java 支持:

text
方法重载

例如:

java
test();

test(String value);

test(int value);

单靠:

text
方法名

无法确定到底是哪一个方法。

所以反射获取方法时需要:

text
方法名
+
参数类型列表

来定位。

8. 通过反射调用方法 ​

获得:

java
Method

以后,可以使用:

java
invoke()

调用。

8.1 调用无参方法 ​

例如:

java
User user =
        new User();

Method method =
        User.class
                .getDeclaredMethod(
                        "sayHello"
                );

method.invoke(user);

相当于:

java
user.sayHello();

8.2 调用有参方法 ​

例如:

java
Method method =
        User.class
                .getDeclaredMethod(
                        "getName",
                        String.class
                );

method.setAccessible(
        true
);

Object result =
        method.invoke(
                user,
                "Java:"
        );

相当于:

java
user.getName(
        "Java:"
);

invoke():

java
method.invoke(
        对象,
        参数1,
        参数2,
        ...
);

其中第一个参数是:

text
在哪个对象上调用这个方法

后面才是真正的方法参数。

8.3 调用 static 方法 ​

如果方法是:

java
public static void hello() {
}

可以:

java
Method method =
        User.class
                .getDeclaredMethod(
                        "hello"
                );

method.invoke(
        null
);

因为:

text
static 方法属于类

不依赖具体对象。

9. 获取构造方法 Constructor ​

反射中的:

java
java.lang.reflect.Constructor

表示:

text
构造方法

假设:

java
public class User {

    private String name;

    public User() {
    }

    public User(
            String name) {

        this.name = name;
    }
}

9.1 获取所有 public 构造方法 ​

java
Constructor<?>[] constructors =
        clazz.getConstructors();

9.2 获取当前类声明的全部构造方法 ​

java
Constructor<?>[] constructors =
        clazz.getDeclaredConstructors();

包括:

text
private

protected

默认权限

public

9.3 获取指定构造方法 ​

无参:

java
Constructor<User> constructor =
        User.class
                .getDeclaredConstructor();

有参:

java
Constructor<User> constructor =
        User.class
                .getDeclaredConstructor(
                        String.class
                );

10. 通过反射创建对象 ​

现代 Java 推荐通过:

java
Constructor.newInstance()

创建对象。

例如:

java
Constructor<User> constructor =
        User.class
                .getDeclaredConstructor();

User user =
        constructor.newInstance();

相当于:

java
User user =
        new User();

有参构造:

java
Constructor<User> constructor =
        User.class
                .getDeclaredConstructor(
                        String.class
                );

User user =
        constructor.newInstance(
                "kerwim"
        );

相当于:

java
User user =
        new User(
                "kerwim"
        );

10.1 不推荐 Class.newInstance() ​

以前经常看到:

java
Object object =
        clazz.newInstance();

这种方式已经被:

text
Deprecated

不推荐使用。

现代 Java 推荐:

java
clazz
        .getDeclaredConstructor()
        .newInstance();

原因之一是:

text
异常处理更加清晰

构造方法选择更加明确

所以现在应该优先:

java
getDeclaredConstructor()
        .newInstance()

11. getXxx 和 getDeclaredXxx 的区别 ​

反射中经常看到两组方法:

text
getFields()

getDeclaredFields()


getMethods()

getDeclaredMethods()


getConstructors()

getDeclaredConstructors()

核心区别可以简单记成:

text
getXxx
    ↓
通常强调 public
    ↓
可能包含继承来的成员


getDeclaredXxx
    ↓
当前类自己声明
    ↓
不管访问修饰符
    ↓
不自动包含父类成员

例如字段:

方法privatepublic父类 public
getFields()✗✓✓
getDeclaredFields()✓✓✗

方法也是类似逻辑。

因此框架扫描实体类字段时,经常使用:

java
getDeclaredFields()

而如果只关心公共 API,可以使用:

java
getMethods()

12. Modifier 修饰符 ​

通过反射还可以判断:

text
public

private

protected

static

final

abstract

等修饰符。

例如:

java
int modifiers =
        field.getModifiers();

再配合:

java
Modifier

判断:

java
Modifier.isPrivate(
        modifiers
);

Modifier.isStatic(
        modifiers
);

Modifier.isFinal(
        modifiers
);

例如:

java
if (
        Modifier.isPrivate(
                field.getModifiers()
        )
) {

    System.out.println(
            "这是 private 字段"
    );
}

所以:

java
Modifier

可以理解为:

解析反射成员修饰符的工具类。

13. 反射获取泛型信息 ​

前面泛型笔记中讲过:

text
Java 泛型存在类型擦除

但并不是:

text
所有泛型信息在运行时都彻底不存在

例如类声明:

java
public class UserService
        implements Service<User> {
}

这里:

text
Service<User>

中的泛型声明信息,可以通过反射 API 获取。

例如:

java
Type[] interfaces =
        UserService.class
                .getGenericInterfaces();

常见反射泛型类型:

text
Type

ParameterizedType

TypeVariable

WildcardType

GenericArrayType

例如:

java
Type type =
        UserService.class
                .getGenericInterfaces()[0];

if (
        type
                instanceof
                ParameterizedType parameterizedType
) {

    Type[] arguments =
            parameterizedType
                    .getActualTypeArguments();
}

因此更准确地说:

类型擦除会擦除对象实例中用于普通类型检查的大部分具体泛型参数,但类文件中某些泛型声明信息仍然可以通过反射读取。

例如不能:

java
if (
        list
                instanceof
                List<String>
) {
}

但可以分析:

text
字段声明

方法声明

父类声明

接口声明

中的泛型签名。

这也是很多框架能够分析:

java
List<User>

Result<User>

Converter<String, Integer>

等声明信息的原因。

14. 反射读取注解 ​

前面 Java 注解.md 中已经讲过:

text
注解本身只是元数据

真正让:

java
@Retention(
        RetentionPolicy.RUNTIME
)

注解发挥作用的常见方式之一就是:

text
反射

例如定义:

java
@Retention(
        RetentionPolicy.RUNTIME
)
@Target(
        ElementType.FIELD
)
public @interface JsonField {

    String value()
            default "";
}

实体:

java
public class User {

    @JsonField(
            "userName"
    )
    private String name;
}

反射:

java
Field field =
        User.class
                .getDeclaredField(
                        "name"
                );

判断:

java
boolean present =
        field.isAnnotationPresent(
                JsonField.class
        );

读取:

java
JsonField annotation =
        field.getAnnotation(
                JsonField.class
        );

读取参数:

java
String value =
        annotation.value();

整个流程:

text
Class
    ↓
获取 Field
    ↓
检查 @JsonField
    ↓
获取注解对象
    ↓
读取注解参数
    ↓
执行对应逻辑

所以:

text
注解
    +
反射

是一组非常经典的组合。

15. 使用反射做一个简单对象打印器 ​

假设:

java
public class User {

    private Long id;

    private String name;

    private int age;

    public User(
            Long id,
            String name,
            int age) {

        this.id = id;
        this.name = name;
        this.age = age;
    }
}

希望写一个通用方法:

text
打印任意对象中的字段

可以:

java
public static void printObject(
        Object object)
        throws IllegalAccessException {

    Class<?> clazz =
            object.getClass();

    Field[] fields =
            clazz.getDeclaredFields();

    for (Field field : fields) {

        field.setAccessible(
                true
        );

        String name =
                field.getName();

        Object value =
                field.get(object);

        System.out.println(
                name
                        + " = "
                        + value
        );
    }
}

调用:

java
User user =
        new User(
                1L,
                "kerwim",
                18
        );

printObject(user);

输出:

text
id = 1
name = kerwim
age = 18

这个方法完全不知道:

text
User 有哪些字段

它是在:

text
运行时

通过反射扫描出来的。

这就是反射真正强大的地方:

代码可以操作自己在编译阶段并不知道具体结构的对象。

16. 反射与框架 ​

反射真正重要的地方不是:

text
平时代替 getter / setter

而是在各种框架内部。

例如:

text
Spring

MyBatis

Jackson

Hibernate

JUnit

Mockito

都大量依赖类似思想。

16.1 Spring IOC ​

假设:

java
@Service
public class UserService {
}

Spring 可以:

text
扫描 Class
    ↓
发现 @Service
    ↓
通过反射分析类
    ↓
寻找构造方法
    ↓
创建 UserService 对象
    ↓
放进 IOC 容器

如果字段还有:

java
@Autowired
private UserMapper userMapper;

框架又可以:

text
扫描字段
    ↓
发现 @Autowired
    ↓
寻找对应 Bean
    ↓
完成依赖注入

这里本质上就是:

text
注解
+
反射
+
IOC

16.2 MyBatis / ORM ​

例如数据库查询结果:

text
id = 1

name = kerwim

age = 18

需要转换为:

java
User

框架可以:

text
获取 User.class
    ↓
创建 User 对象
    ↓
读取字段 / Setter
    ↓
根据字段名映射数据库列
    ↓
设置属性值

底层同样离不开:

text
反射

16.3 Jackson JSON 序列化 ​

对象:

java
User user;

转换:

json
{
  "id": 1,
  "name": "kerwim"
}

Jackson 需要知道:

text
对象有哪些字段

有哪些 getter

字段有没有注解

字段类型是什么

这些信息都可以通过:

text
反射

获得。

16.4 JUnit ​

例如:

java
@Test
public void testCreateUser() {
}

JUnit 可以:

text
扫描测试类
    ↓
寻找 @Test
    ↓
获得对应 Method
    ↓
method.invoke(...)

从而自动执行测试方法。

所以:

反射是很多 Java 框架实现“自动化”和“动态化”的基础设施之一。

17. 反射与动态代理 ​

Java 的:

text
JDK 动态代理

也和反射关系非常密切。

例如:

java
Proxy.newProxyInstance(
        classLoader,
        interfaces,
        invocationHandler
);

其中:

java
InvocationHandler

的核心方法:

java
Object invoke(
        Object proxy,
        Method method,
        Object[] args
)

这里直接拿到了:

java
Method

对象。

然后可以:

java
method.invoke(
        target,
        args
);

最终形成:

text
调用代理对象
    ↓
InvocationHandler
    ↓
获得 Method
    ↓
前置逻辑
    ↓
反射调用目标方法
    ↓
后置逻辑

这也是:

text
AOP

事务

日志

权限

RPC

等技术的重要基础之一。

动态代理本身内容比较多,可以后面在:

text
代理模式 / AOP

中单独展开。

18. 反射的缺点 ​

反射非常强大,但不意味着:

text
所有代码都应该用反射

它也有明显缺点。

18.1 可读性降低 ​

普通代码:

java
user.setName(
        "kerwim"
);

非常直接。

反射:

java
Method method =
        User.class
                .getDeclaredMethod(
                        "setName",
                        String.class
                );

method.invoke(
        user,
        "kerwim"
);

明显更复杂。

18.2 编译期检查能力下降 ​

普通代码:

java
user.sayHello();

如果方法不存在:

text
编译阶段直接报错

而反射:

java
clazz.getDeclaredMethod(
        "sayHelo"
);

字符串写错:

text
编译阶段可能完全没问题
    ↓
运行时
    ↓
NoSuchMethodException

也就是说:

text
一部分错误
    ↓
从编译期推迟到了运行时

18.3 性能存在额外开销 ​

反射需要:

text
查找成员

访问检查

参数包装

动态调用

所以相比普通直接调用通常存在额外开销。

不过现代 JVM 对反射已经进行了大量优化。

因此不应该简单说:

text
反射性能非常差
绝对不能使用

正确理解应该是:

普通业务代码没有必要为了炫技使用反射,但框架和基础设施中合理使用反射完全正常。

真正性能敏感的框架还可能使用:

text
缓存反射对象

MethodHandle

字节码生成

代码生成

进一步优化。

18.4 破坏封装 ​

通过:

java
setAccessible(true)

可能访问:

text
private 字段

private 方法

这会削弱:

text
private

原本提供的封装边界。

所以反射应该:

text
有明确目的地使用

而不是在普通业务代码中随意绕过访问权限。

19. 反射的常见异常 ​

使用反射时经常会遇到一些异常。

19.1 ClassNotFoundException ​

java
Class.forName(...)

找不到目标类。

19.2 NoSuchFieldException ​

java
getDeclaredField(...)

找不到字段。

19.3 NoSuchMethodException ​

java
getDeclaredMethod(...)

getDeclaredConstructor(...)

找不到对应方法或构造方法。

19.4 IllegalAccessException ​

当前反射操作没有访问权限。

19.5 InstantiationException ​

某些反射创建对象场景中无法完成实例化。

19.6 InvocationTargetException ​

通过:

java
Method.invoke()

或者:

java
Constructor.newInstance()

调用目标代码时,如果目标方法本身抛出异常,反射 API 可能使用:

java
InvocationTargetException

进行包装。

例如:

java
try {

    method.invoke(
            object
    );

} catch (
        InvocationTargetException e) {

    Throwable cause =
            e.getCause();
}

真正业务方法抛出的异常通常可以从:

java
e.getCause()

获得。

这一点非常重要:

text
InvocationTargetException
    ↓
反射调用产生的包装异常


getCause()
    ↓
目标方法真正抛出的异常

19.7 InaccessibleObjectException ​

现代 Java 模块强封装下:

java
setAccessible(true)

可能无法突破模块边界。

这时可能出现:

text
InaccessibleObjectException

说明:

text
反射访问被模块系统限制

20. 反射使用原则 ​

反射适合:

text
框架底层

通用工具

对象映射

序列化

IOC

ORM

插件系统

注解处理

动态代理

普通业务代码如果已经明确知道类型:

java
User user =
        new User();

就没必要写成:

java
Class<?> clazz =
        User.class;

Object user =
        clazz
                .getDeclaredConstructor()
                .newInstance();

所以可以简单记:

text
类型明确
    ↓
优先普通 Java 调用


类型运行时才能确定
    ↓
考虑反射

另外,如果需要重复使用反射对象:

text
Field

Method

Constructor

通常可以:

text
缓存

避免每次重复查找。

例如框架经常:

text
第一次扫描
    ↓
缓存 Method / Field 元数据
    ↓
后续重复使用

而不是每次请求都重新完整扫描类。

21. 小结 ​

Java 反射的核心入口:

java
Class

获取 Class:

text
User.class

user.getClass()

Class.forName(...)

通过 Class 可以获取:

text
Field
    ↓
字段


Method
    ↓
方法


Constructor
    ↓
构造方法


Annotation
    ↓
注解

然后可以进行:

text
读取字段

修改字段

调用方法

创建对象

读取注解

分析类型

最重要的一组区别:

text
getXxx()
    ↓
通常获取 public
    ↓
可能包含继承成员


getDeclaredXxx()
    ↓
当前类自己声明
    ↓
包含 private
    ↓
不自动包含父类成员

反射和其他 Java 技术之间的关系:

text
注解
    +
反射
    ↓
读取运行时元数据


反射
    +
IOC
    ↓
动态创建和注入对象


反射
    +
ORM
    ↓
数据库数据映射对象


反射
    +
动态代理
    ↓
AOP / 事务 / 日志

整个反射过程可以浓缩成:

text
获得 Class
    ↓
分析类结构
    ↓
获得 Field / Method / Constructor
    ↓
动态读取 / 调用 / 创建

一句话概括:

反射就是让 Java 程序能够在运行时观察并操作类型本身,是 Java 框架实现动态化能力的重要基础。

Noctiflorous Garden · MIT License