臭名昭著aTrust隔离计划1.臭名昭著aTrust隔离计划2.VPN原理与aTrust隔离网络实践3.docker-easyconnect到底做了什么4.TUN(tunnel-隧道-虚拟网卡)模式
个人项目
personal(个人展示)GitHub Actions 自动部署个人展示项目
基于Quartz搭建的个人博客1.Quartz个人博客使用教程2.使用 rsync 增量部署 Quartz 博客3.使用 GitHub Actions 自动部署 Quartz 博客5.域名绑定6.CDN加速7.Google 搜索收录排查:为什么关键词搜得到别人,却搜不到我的网站?Obsidian、双向链接与知识图谱
架构设计
分布式分布式事务RPC原理
旅行
行程攻略青岛三日游行程攻略
前端
NginxNginx配置与反向代理入门
Nodenpm与npx的区别
数据库
mysql01.MySQL基础语法与约束02.MySQL数据库类型03.MySQL多表查询04.MySQL 索引05.MySQL 事务06.MySQL 隔离级别07.MySQL MVCC08.MySQL 锁09.EXPLAIN10.慢查询与 SQL 优化
mysql面试题01.索引问题02.事务与隔离级别03.锁机制04.存储引擎与性能优化05.用户权限、字符集与备份恢复
mysql实战01.EXPLAIN实战02.测试环境菜单误删排查与恢复03.MySQL IN 详解
网络
网络安全1.无线监听网卡苹果M系列虚拟机安装Kali Linux教程3.kali怎么使用RTL8812AU4.kali基础命令
网络基础进程之间怎样通过网络传递数据
网址常用网址
中间键
esElasticsearch 入门
mq
RabbitMQRabbitMQ高级RabbitMQ基础
1. MQ 基础2. MQ 核心角色与代码对应3. MQ Consumer Group 与消费模式4. SOFAMQ 账户变更代码实践5. MQ 可靠性问题6. 消息幂等
redis缓存击穿、穿透与雪崩Redis 分布式锁Redis 缓存问题Redis 数据结构
AI
使用说明
第三方插件&技能简介Archify使用与安装指南geekgeekRun使用OfferFlow使用指南Ponytail使用指南Skills Manager使用指南
CodexCodex CLI与IDE区别及使用指南Codex Hook单独配置与提交通知Codex MCP安装与使用指南Codex第三方插件安装与使用指南
Agent开发0-学习路线1.LLM、Token、上下文窗口与模型参数2.大模型 API、请求参数与响应结构3.Spring AI ChatClient4.Prompt、System Prompt、Prompt 模板6.结构化输出、JSON Schema7.SSE 流式响应8.会话 ID、聊天记录、Redis9.超时、重试、限流、降级10.*完成可运行聊天接口11.Function Calling、Tool Calling 原理12.用 Java 方法定义 Tool13.工具参数设计和校验14.实现订单查询工具15.实现物流查询工具16.实现库存查询工具17.多个工具自动选择18.工具异常、超时、重试
diy主机
主机零件学习1.CPU2.主板3.内存4.显卡5.硬盘6.散热7.风扇8.机箱热门型号价格表
捡垃圾diy-13600kf+5060diy-14600kf+5070
English高频词汇英文基础语法简单句子音标
GitGit常用命令Sublime Merge 使用指南
Java
工具库Caffeine本地缓存Reactor教程
工作实践如何使用@Async线程池使用与排查线上服务 CPU 使用率过高排查压测下RPC接口大量失败排查记录
基础1.HashMap2.ConcurrentHashMap3.HashSet4.final5.static6.volatile7.AtomicInteger8.String动态代理反射锁与线程同步线程注解
面试题1.HashMap 存储在方法内是线程安全的吗2.ArrayList参数传递与引用重新赋值3.== 和 equals 的区别、hashcode4.重载和重写的区别5.String、StringBuffer、StringBuilder区别及使用场景6.接口和抽象类7.ArrayList 与 LinkedList 的区别和扩容机制8.HashMap 和 Hashtable 的区别及底层实现9.Object 和 Objects 的区别10.ThreadLocal的原理和使用场景11.字节码是什么及作用12.Java类加载器有哪些13.双亲委派模型14.Java异常体系15.GC是什么16.GC如何判断对象可以被回收
面试总结0.HR面试模板1.面试一2.面试二3.面试三笔试
网络与 IO(黑马程序员-纯理论)01-网络编程基础02-IO模型与零拷贝03-BIO04-NIO基础与缓冲区05-NIO通道与选择器06-AIO
juc00-目录01-进程与线程02-线程同步与通信03-并发设计模式04-Java内存模型05-CAS与原子类06-ThreadLocal07-阻塞队列08-线程池使用与原理09-任务调度10-ForkJoin与享元模式11-AQS12-ReentrantLock13-读写锁14-并发协作工具15-ConcurrentHashMap16-CopyOnWrite与同步容器17-SkipListMap18-非阻塞队列
juc(工作常见)Java并发编程三个重点拆解
jvm1. JVM运行时数据区
Spring1.Spring IoC、DI与构造方法注入CGLIB、Byte Buddy 与 Spring AOPSpring 常用注解详解Spring Bean 生命周期
Spring MVC异常处理机制
linux常用命令
mac快捷键终端常用操作
python
基础01.Python是什么,为什么要安装Python02.Python项目中.venv是什么03.Python基础语法
基础实战sql替换
HTTPXHRMS员工详情接口调用(Python HTTPX)
windowswindows激活

一、💡 一句话理解

核心结论

反射(Reflection)让 Java 程序在运行时查看一个类有哪些字段、方法和构造器,并在必要时创建对象、读取字段或调用方法。

平时写代码时,我们直接写 person.sayHello();反射则可以先通过字符串找到 sayHello 方法,再决定要不要调用它。

二、🧭 反射是什么

2.1 为什么需要反射

普通调用时,类名和方法名都在写代码时确定:

Person person = new Person("小明");
person.sayHello();

有些场景下,程序运行前并不知道要处理哪个类或哪个方法。例如框架需要:

  • 根据配置创建对象;
  • 扫描带某个注解的方法;
  • 将数据库查询结果填入对象字段;
  • 根据请求地址调用对应的方法。

这时就需要反射。JDK 的 java.lang.reflect 包提供了运行时获取类、字段、方法、构造器信息并操作它们的能力。Java 反射包文档

2.2 反射中的四个核心对象

对象它代表什么常见用途
Class一个类本身查看类名、获得字段、方法、构造器。
Field类中的一个字段读取或修改对象属性。
Method类中的一个方法调用指定方法。
Constructor类中的一个构造器在运行时创建对象。

可以把它们理解为:Class 是一张“类的说明书”,其中列出了字段、方法和构造器。

三、⚙️ 最基础的反射操作

3.1 获取 Class 对象

Class 是反射的入口。最常用的是 .class:

public class ReflectionStartDemo {
 
    public static void main(String[] args) {
        // 先按普通方式创建 Person 对象并调用方法。
        Person person = new Person("小明");
        person.sayHello();
 
        // 再取得 Person 类对应的 Class 对象。
        Class<Person> personClass = Person.class;
        System.out.println("类名:" + personClass.getName());
    }
}
 
class Person {
    private final String name;
 
    public Person(String name) {
        this.name = name;
    }
 
    public void sayHello() {
        System.out.println(name + ":你好!");
    }
}

运行后会输出:

小明:你好!
类名:Person

这里的 person 是一个具体的人对象,所以可以直接调用 person.sayHello();personClass 则是 Person 这个类的说明书对象,所以可以读取类名、查找方法或构造器。

也可以从已有对象获得:

Person person = new Person("小明");
Class<?> personClass = person.getClass();

Class 的实例代表运行中的类或接口;JVM 会在加载类时创建相应的 Class 对象。Java Class 文档

这句话的意思是:当程序第一次需要使用 Person 这个类时,JVM 会把它编译后的 Person.class 文件读进内存,并自动创建一个 Class<Person> 对象,用来描述 Person。

这个 Class<Person> 不是 Person 的普通实例,而是“Person 类的说明书对象”。它记录了 Person 有哪些字段、方法和构造器,反射正是通过它查看类的结构。

Person.java
   ↓ 编译
Person.class
   ↓ JVM 加载
Class<Person> 对象
   ↓
反射查看 Person 的结构

不需要自己创建 Class 对象;JVM 会自动创建。Person.class 或 person.getClass() 都是在取得这个已经存在的对象。

3.2 获取并调用方法

在上一个示例的 main 方法中,已经有了 person 和 personClass。下面的代码会查找 sayHello(),再通过反射执行它:

import java.lang.reflect.Method;
 
// 获取名为 sayHello、没有参数的公开方法。
Method method = personClass.getMethod("sayHello");
 
// 在 person 对象上执行这个方法。
method.invoke(person);

因此,下面两行的效果相同:

person.sayHello();       // 编写代码时已确定要调用 sayHello
method.invoke(person);   // 运行时根据方法名 "sayHello" 查找后再调用

getMethod 查找公开方法;invoke 执行方法。若方法有参数,查找时要写参数类型,调用时要传参数值:

Method method = Person.class.getMethod("greet", String.class);
method.invoke(person, "小红");

3.3 获取并读取字段

import java.lang.reflect.Field;
 
// 获取公开字段 age。
Field field = Person.class.getField("age");
 
// 从 person 对象上读取 age 的值。
int age = (int) field.get(person);

getField 只查找公开字段。getDeclaredField 可以查找本类声明的字段,包括 private 字段,但访问私有字段需要额外处理,且会受到 Java 封装和模块规则限制。日常业务代码应优先使用正常的 getter/setter,不要为了方便滥用反射修改私有数据。

3.4 获取构造器并创建对象

import java.lang.reflect.Constructor;
 
// 获取参数为 String 的公开构造器。
Constructor<Person> constructor = Person.class.getConstructor(String.class);
 
// 用该构造器创建对象。
Person person = constructor.newInstance("小明");

四、🚀 完整小例子:查看、创建和调用

4.1 前置准备

需要 JDK 8 或更高版本。新建空目录,并创建文件 ReflectionDemo.java。

4.2 完整代码

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
 
public class ReflectionDemo {
 
    /**
     * 被反射操作的普通类。
     */
    static class Person {
        // 公开字段,便于演示 Field.get()。
        public int age;
 
        private final String name;
 
        // 普通构造器,稍后会通过反射调用它。
        public Person(String name) {
            this.name = name;
        }
 
        // 普通方法,稍后会通过反射调用它。
        public void sayHello() {
            System.out.println(name + ":你好!");
        }
 
        // 带参数的方法,演示反射调用时如何传入参数。
        public String greet(String guest) {
            return name + " 对 " + guest + " 说:你好";
        }
    }
 
    public static void main(String[] args) throws ReflectiveOperationException {
        // 1. 获得 Person 类的 Class 对象。
        Class<Person> personClass = Person.class;
        System.out.println("类名:" + personClass.getName());
 
        // 2. 普通方式创建对象,并直接调用方法。
        Person person = new Person("小明");
        System.out.println("普通调用:");
        person.sayHello();
 
        // 3. 找到 sayHello() 方法,并在 person 对象上通过反射调用。
        Method sayHelloMethod = personClass.getMethod("sayHello");
        System.out.println("反射调用:");
        sayHelloMethod.invoke(person);
 
        // 4. 找到 String 参数的构造器,并通过反射创建另一个 Person 对象。
        Constructor<Person> constructor = personClass.getConstructor(String.class);
        Person reflectedPerson = constructor.newInstance("小红");
 
        // 5. 找到公开字段 age,并为反射创建的对象赋值、读取。
        Field ageField = personClass.getField("age");
        ageField.set(reflectedPerson, 18);
        System.out.println("年龄:" + ageField.get(reflectedPerson));
 
        // 6. 找到 greet(String) 方法,并在反射创建的对象上调用它。
        Method greetMethod = personClass.getMethod("greet", String.class);
        Object result = greetMethod.invoke(reflectedPerson, "小明");
        System.out.println(result);
    }
}

4.3 启动和验证

在文件所在目录运行:

javac ReflectionDemo.java
java ReflectionDemo

预期输出中的类名会包含外部类名:

类名:ReflectionDemo$Person
普通调用:
小明:你好!
反射调用:
小明:你好!
年龄:18
小红 对 小明 说:你好

这个例子依次完成了:普通方式创建对象并调用方法 → 通过 Class 查找方法并调用 → 查找构造器并创建对象 → 查找字段并读写 → 调用带参数的方法。

五、🔗 反射和自定义注解的关系

反射依赖类加载机制先把类变成 JVM 中的 Class 对象;加载、链接、初始化以及类加载器委派过程可以参考 2. JVM的类加载机制。

前一篇 注解 中的 @NeedLogin 本身只是标签。反射可以在运行时检查某个方法有没有这个标签:

import java.lang.reflect.Method;
 
Method method = UserService.class.getMethod("viewProfile");
 
// true 表示该方法标了 @NeedLogin。
boolean needLogin = method.isAnnotationPresent(NeedLogin.class);
 
if (needLogin) {
    System.out.println("调用前先检查是否登录");
}

这就是“注解负责说明,程序负责执行”的实现基础:

方法写上 @NeedLogin
        ↓
反射取得 Method 对象
        ↓
isAnnotationPresent 判断是否有标签
        ↓
程序执行登录检查或其他规则

要让运行时反射读到自定义注解,定义注解时必须写:

@Retention(RetentionPolicy.RUNTIME)

六、📌 什么时候使用反射

反射适合用于“运行前无法完全确定要操作哪个类或方法”的情况:

场景反射做什么
依赖注入框架查找构造器、字段或方法,再注入对象。
JSON 转对象找到字段,把 JSON 数据写入对象。
注解处理找到带特定注解的类或方法,执行对应规则。
测试工具执行符合规则的测试方法。

如果类和方法在写代码时已明确,直接调用通常更简单、类型更安全,也更容易阅读;不要为了使用反射而使用反射。

七、📝 总结

  • 反射让程序在运行时查看并操作类的结构。
  • Class 是入口;Field、Method、Constructor 分别对应字段、方法和构造器。
  • getMethod/invoke 可以查找并调用方法;getField/get/set 可以读写公开字段。
  • 反射可以读取带 RUNTIME 保留策略的自定义注解。

记忆句:普通代码直接调用;反射先找到,再调用。

八、📚 官方资料