java反射原理:
Java 的反射(Reflection)本质上依靠的是 JVM 在运行时保存的类的元数据(Metadata) ,这些元数据来自 .class
文件中的 Class 文件结构 ,并由 Class 对象 + java.lang.reflect 包 提供访问入口。
我给你分成几个层次解释,这样你能从底层到应用都搞明白。
1. 反射的定义
反射就是在 运行时 动态获取类的信息,并操作类的属性、方法、构造器,甚至创建对象。
它的特点是:
-
不需要在编译时就知道具体的类;
-
运行时可以根据类名、方法名等字符串进行操作。
2. 反射依靠的底层机制
(1)Class 文件中的元数据
当你用 javac
编译 .java
源文件后,会生成 .class
文件。
这个 .class
文件遵循 Java Class File Format(Java虚拟机规范),里面包含了:
-
类的名称、父类名称
-
成员变量(字段)的名称、类型、修饰符
-
方法签名(方法名、参数类型、返回值类型)
-
注解信息
-
常量池(字符串、类引用等)
这些信息就是 类的元数据(Metadata)。
(2)JVM 的 方法区/元空间(Metaspace)
-
当 JVM 加载类 时(ClassLoader 负责),会把
.class
文件解析后,将类的元数据存储到 方法区(Java 8 以前)或元空间(Java 8 以后)。 -
这个元数据中保存了类的:
-
字段信息
-
方法信息
-
构造方法信息
-
注解信息
-
-
反射就是读取这些元数据来工作的。
(3)java.lang.Class
类
-
每个加载到 JVM 的类,在内存中都会有一个唯一的
Class
对象表示它的类型信息。 -
Class
对象是反射的入口,你可以通过以下方式获得:Class<?> clazz1 = Class.forName("com.example.User"); Class<?> clazz2 = User.class; Class<?> clazz3 = userInstance.getClass();
-
这个
Class
对象中保存了指向元数据的引用,反射 API 就是通过它去查询和操作元数据。
(4)java.lang.reflect
包
-
反射 API 主要在
java.lang.reflect
包下,比如:-
Field
(字段) -
Method
(方法) -
Constructor
(构造方法)
-
-
它们提供了读写属性、调用方法、创建对象等功能。
-
这些类会通过
native
方法调用 JVM 的底层接口(HotSpot VM 中是 C++ 实现)去访问方法区/元空间的元数据。
3. 流程图
[.java 源代码]
↓ 编译
[.class 字节码文件]
↓ 类加载器解析(ClassLoader)
[方法区/元空间存放类元数据]
↓
[Class对象] ←→ java.lang.reflect API
↓
反射操作(获取字段/方法/构造器、实例化对象、调用方法)
4. 示例:反射依靠元数据访问类
public class User {
private String name;
public void sayHello() {
System.out.println("Hello, " + name);
}
}
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
// 1. 获取Class对象(依赖类加载器和元数据)
Class<?> clazz = Class.forName("User");
// 2. 创建实例
Object obj = clazz.getDeclaredConstructor().newInstance();
// 3. 获取字段并修改(依赖元数据)
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(obj, "Tom");
// 4. 获取方法并调用(依赖元数据)
Method method = clazz.getDeclaredMethod("sayHello");
method.invoke(obj); // 输出:Hello, Tom
}
}
5. 总结
Java 反射依靠的是:
-
.class
文件中的 类元数据(字段、方法、构造器等信息)。 -
JVM 类加载后存储在 方法区/元空间 的类结构。
-
Class
对象作为元数据的入口。 -
java.lang.reflect
包 通过 JVM 本地方法访问元数据并操作。
💡 换句话说:
反射不是凭空出现的,它是 JVM 通过 类加载 + 元数据存储 + Class对象 + 反射API 这一整套机制支撑的。
为什么int表示范围,正数比负数少1:
因为用补码表示
