【Java基础】反射

参考文章:
前言
Java反射机制赋予了语言强大的元编程能力,让程序能够“自我认知”和动态调整。这在现代软件开发中不可或缺,尤其是在追求松耦合和可配置性的场景下。尤其建议初学者学习反射,可以:
- 学习类加载机制
- 学习“元”编程思想
- 便于学习Java主流开发框架原理
一、什么是反射?
反射是指Java程序在运行时能够检查、访问和修改类、接口、字段、方法和构造函数的信息。简单说,就是让你的代码能“看清”自己或别的类的内部结构,甚至动态地调用方法或改变属性。
- 核心包:反射主要通过
java.lang.reflect包实现,比如Class、Field、Method、Constructor等类。 - 为什么叫“反射”? 因为它像一面镜子,让程序能“反射”出自己的样子。
在Java中,一切都是对象,包括类本身。每个类在加载时都会有一个对应的Class对象,这个对象就是反射的入口。
简而言之:只要你给我一个类我就能获取这个类的所有信息。
二、什么是Class类
Class 类的官方定义如下:
Class 是 Java 反射机制的核心类,位于 java.lang 包中。它的实例表示正在运行的 Java 应用程序中的类(class)、接口(interface)、枚举(enum)、数组(array)、基本类型(如 int、void) 等类型的 运行时元数据。
更直白地说:
- 每个类型在 JVM 中都有且仅有一个对应的 Class 对象。
- 这个对象由 JVM 自动创建,不是通过 new 生成的。
- 它保存了该类型的所有结构信息:名称、字段、方法、构造器、修饰符、父类、实现的接口、注解等。
三、一个类是怎么被加载的

前提:JVM内存模型
Maven从入门到精通
要真正理解 Java 反射,必须搞清楚 类加载 和 反射机制 之间的关系。
JVM 先通过类加载器把
.class文件变成内存中的Class对象,然后你的代码通过这个Class对象去动态获取类的结构并操作实例,也就是反射。
从 .java 到堆中对象,本质是:源码 → 字节码 → 类元数据(方法区)→ Class 对象(堆)→ 实例对象(堆),让我带你一步步剖析
1.编译(Compile)
执行命令:mvn compile ->调用 javac 编译所有 .java 文件 -> 输出 .class 文件到 target/classes/
2.加载(Loading)—— JVM 启动后首次使用类时
图中类加载器加载部分:
- 读取类的字节码文件也就是class文件
- 解析 为 JVM 内部数据结构
- 在方法区分配内存,存储:类名,方法,字段等类的基本信息
- 在堆中创建
java.lang.Class对象
→ 这个对象是“类的运行时表示”,内部持有一个指向方法区元数据的 native 指针
3.链接(LInking)
分为三步:
① 验证(Verification)
- 检查字节码是否合法
② 准备(Preparation)
- 为 static 变量分配内存
- 设置 默认初始值
public static int x = 100; // 此时 x = 0,不是 100!
③ 解析(Resolution,可选延迟)
- 将符号引用(如
"java/lang/Object")转为直接引用
4.初始化(Initialization)
- 执行
<clinit>方法:包括所有 static 变量的显式赋值 和 static {} 块public static int x = 100; // ← 现在 x = 100 static { System.out.println("Initializing..."); } - 父类先于子类初始化
至此,类才算加载完成,可以开始使用反射了,接下来讲讲如何使用。
四、 如何使用Java反射
Java反射的使用主要通过java.lang.reflect包下的类来实现。下面以代码示例说明基本操作。
- 获取Class对象:
有三种常见方式:- 通过类名:
Class<?> clazz = Class.forName("java.lang.String"); - 通过对象实例:
Class<?> clazz = "hello".getClass(); - 通过类字面量:
Class<?> clazz = String.class;
- 通过类名:
有了Class对象你就可以开始挖掘,类的信息了,如类名、修饰符、方法、字段、接口、注解等。
-
访问字段:
import java.lang.reflect.Field; public class ReflectionExample { private String name = "Java"; public static void main(String[] args) throws Exception { Class<?> clazz = ReflectionExample.class; Field field = clazz.getDeclaredField("name"); // 获取私有字段 field.setAccessible(true); // 绕过访问控制 ReflectionExample instance = new ReflectionExample(); field.set(instance, "Reflection"); // 修改字段值 System.out.println(instance.name); // 输出: Reflection } }这里,我们通过
getDeclaredField获取私有字段,并使用setAccessible(true)忽略访问修饰符。 -
调用方法:
import java.lang.reflect.Method; public class ReflectionExample { public void greet(String message) { System.out.println("Hello, " + message); } public static void main(String[] args) throws Exception { Class<?> clazz = ReflectionExample.class; Object instance = clazz.getDeclaredConstructor().newInstance(); // 创建实例 Method method = clazz.getMethod("greet", String.class); // 获取方法 method.invoke(instance, "World"); // 调用方法,输出: Hello, World } }
另外, invoke方法允许动态执行任何方法,支持参数传递。
需要注意的是,反射操作可能会抛出异常,如NoSuchMethodException或IllegalAccessException,因此实际代码中应使用try-catch块。
五、举个栗子
你是一个程序员你想实现一个简单的 Web 服务,支持两个接口:
// 接口1: /user/123 → 返回用户
//接口2: /order/456 → 返回订单
没有反射时,你只能这样写主逻辑:
public class WebServer {
public void handleRequest(String path) {
if (path.startsWith("/user/")) {
String id = extractId(path);
UserController uc = new UserController();
User user = uc.getUser(id); // ← 硬编码调用
sendJsonResponse(user);
}
else if (path.startsWith("/order/")) {
String id = extractId(path);
OrderController oc = new OrderController();
Order order = oc.getOrder(id); // ← 又一处硬编码
sendJsonResponse(order);
}
else {
send404();
}
}
}
- 每加一个 Controller,就要改
handleRequest方法;
有了反射 + 注解
你只需要写:
@RestController
public class UserController {
@GetMapping("/user/{id}")
public User getUser(@PathVariable String id) { ... }
}
@RestController
public class OrderController {
@GetMapping("/order/{id}")
public Order getOrder(@PathVariable String id) { ... }
}
而 Spring 启动时自动做这件事:
// 扫描所有带 @RestController 的类
for (Class<?> clazz : allClassesWithRestController) {
Object instance = clazz.getDeclaredConstructor().newInstance(); // 反射创建对象
for (Method method : clazz.getMethods()) {
if (method.isAnnotationPresent(GetMapping.class)) {
String path = method.getAnnotation(GetMapping.class).value()[0];
// 把路径和“方法调用逻辑”存起来
routeMap.put(path, (request) -> {
String id = extractIdFromPath(request.path);
return method.invoke(instance, id); // ← 反射调用!
});
}
}
}
// 处理请求时:
public void handleRequest(String path) {
Handler handler = routeMap.get(matchedPath);
Object result = handler.handle(request); // 自动调用对应方法
sendJsonResponse(result);
}
这就是反射的强大之处:它让程序能在运行时动态地“看见”并“操控”任意类的结构和行为,即使这些类在编写代码时根本不存在。
结语:反射的功能很强大,赋予开发者动态处理类的能力,在框架和插件领域功不可没,但是使用时需要考虑性能,安全,可读性等问题
更多推荐


所有评论(0)