Java反射深度解析笔记
📖 目录
基础概念
什么是反射?
Java反射是一个"透视镜",让程序在运行时能够:
- 查看类的内部结构
- 获取类的属性和方法
- 创建对象并调用方法
- 绕过访问控制限制
Class对象
每个类都有一个对应的Class对象,它是这个类的"身份证"
// 获取Class对象的三种方式
Class<?> clazz1 = String.class; // 方式1:类字面量
Class<?> clazz2 = "hello".getClass(); // 方式2:对象方法
Class<?> clazz3 = Class.forName("java.lang.String"); // 方式3:类名字符串
反射的基本用法
1. 创建对象
public class Student {
private String name;
private int age;
public Student() {}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
// 使用反射创建对象
Class<?> clazz = Student.class;
Student student = (Student) clazz.newInstance(); // 调用无参构造器
// 使用有参构造器
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Student student2 = (Student) constructor.newInstance("张三", 20);
2. 获取和操作属性
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
Class<?> clazz = Student.class;
Student student = (Student) clazz.newInstance();
// 获取私有属性
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 设置可访问私有属性
// 设置属性值
nameField.set(student, "小明");
// 获取属性值
String name = (String) nameField.get(student);
System.out.println("姓名: " + name);
}
}
3. 调用方法
public class Student {
private String name;
public void sayHello() {
System.out.println("Hello, I'm " + name);
}
private void study(String subject) {
System.out.println("正在学习: " + subject);
}
}
// 反射调用方法
Class<?> clazz = Student.class;
Student student = (Student) clazz.newInstance();
// 调用公共方法
Method sayHelloMethod = clazz.getMethod("sayHello");
sayHelloMethod.invoke(student);
// 调用私有方法
Method studyMethod = clazz.getDeclaredMethod("study", String.class);
studyMethod.setAccessible(true);
studyMethod.invoke(student, "Java");
底层原理
JVM内存结构
JVM内存布局:
┌─────────────────┐
│ 方法区/元空间 │ ← 类的元数据存储在这里
├─────────────────┤
│ 堆内存 │ ← 对象实例存储在这里
├─────────────────┤
│ 栈内存 │ ← 方法调用和局部变量
└─────────────────┘
类加载过程
类加载过程:
加载 → 链接(验证→准备→解析) → 初始化
方法区中存储的类信息:
┌─────────────────────────────────┐
│ Student类的元数据结构 │
├─────────────────────────────────┤
│ • 类名: com.example.Student │
│ • 父类: java.lang.Object │
│ • 接口列表: [] │
│ • 访问标志: public │
│ • 字段表: │
│ - name: String, private │
│ - age: int, private │
│ • 方法表: │
│ - study(): void, public │
│ - <init>(): void, public │
│ • 常量池 │
│ • 属性表 │
└─────────────────────────────────┘
Class对象内部结构
// Class对象内部结构(简化版)
public final class Class<T> {
// 指向方法区中类元数据的指针
private transient ClassLoader classLoader;
private transient Class<?> componentType;
// 缓存反射信息,提高性能
private volatile transient Field[] declaredFields;
private volatile transient Method[] declaredMethods;
private volatile transient Constructor<T>[] declaredConstructors;
// 类的元数据信息
private transient String name;
private transient int modifiers;
}
对象内存布局
对象内存布局:
┌─────────────────┐
│ 对象头(8字节) │ ← Mark Word + Class指针
├─────────────────┤
│ 实例数据 │ ← 字段值存储区域
│ name: "张三" │ ← String引用(8字节)
│ age: 20 │ ← int值(4字节)
├─────────────────┤
│ 对齐填充 │ ← 保证8字节对齐
└─────────────────┘
底层Native实现
// JVM中的C++代码(简化版)
// 获取字段值
oop Reflection::field_get(jfieldID field_id, oop obj) {
// 1. 获取字段偏移量
int offset = field_id->offset();
// 2. 根据字段类型读取内存
BasicType field_type = field_id->field_type();
switch (field_type) {
case T_OBJECT:
return obj->obj_field(offset); // 读取对象引用
case T_INT:
return java_lang_Integer::create(obj->int_field(offset));
case T_LONG:
return java_lang_Long::create(obj->long_field(offset));
}
}
反射访问完整过程
// 完整的反射访问过程
public class ReflectionProcess {
public static void main(String[] args) throws Exception {
Student student = new Student();
// 1. 获取Class对象 - 从对象头中获取类型指针
Class<?> clazz = student.getClass();
// 2. 查找字段 - 遍历类元数据中的字段表
Field nameField = clazz.getDeclaredField("name");
// 3. 设置可访问 - 修改AccessibleObject的override标志
nameField.setAccessible(true);
// 4. 设置值 - 计算字段偏移量,直接写入内存
nameField.set(student, "小明");
// 5. 获取值 - 根据偏移量从内存读取
String name = (String) nameField.get(student);
}
}
性能优化机制
1. 缓存机制
// Class对象中的缓存
private volatile transient Field[] declaredFields;
// 第一次调用时创建,后续直接返回缓存
public Field[] getDeclaredFields() throws SecurityException {
if (declaredFields == null) {
declaredFields = getDeclaredFields0(false);
}
return declaredFields.clone();
}
2. 方法访问器优化
// 反射调用优化:从解释执行到编译执行
// 前15次调用使用native方法
// 第16次开始生成字节码,直接调用
Method method = clazz.getMethod("toString");
for (int i = 0; i < 20; i++) {
method.invoke(obj); // 第16次后性能显著提升
}
3. Unsafe类:最接近底层的操作
import sun.misc.Unsafe;
public class UnsafeExample {
private static Unsafe unsafe;
public static void directMemoryAccess() {
Student student = new Student("张三", 20);
try {
// 获取字段的偏移量
Field nameField = Student.class.getDeclaredField("name");
long offset = unsafe.objectFieldOffset(nameField);
// 直接从内存读取字段值
Object value = unsafe.getObject(student, offset);
System.out.println("通过Unsafe读取: " + value);
// 直接向内存写入字段值
unsafe.putObject(student, offset, "李四");
} catch (Exception e) {
e.printStackTrace();
}
}
}
实际应用场景
1. 配置文件驱动
// 从配置文件读取类名,动态创建对象
String className = "com.example.UserService";
Class<?> clazz = Class.forName(className);
Object service = clazz.newInstance();
2. 框架开发(类似Spring)
// 框架扫描注解,自动创建对象
@Service
public class UserService {
// ...
}
// 框架代码
if (clazz.isAnnotationPresent(Service.class)) {
Object bean = clazz.newInstance();
// 注册到容器中
}
3. 通用工具方法
public class ObjectUtils {
// 通用的对象复制方法
public static void copyProperties(Object source, Object target) {
Class<?> sourceClass = source.getClass();
Class<?> targetClass = target.getClass();
Field[] fields = sourceClass.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
Object value = field.get(source);
Field targetField = targetClass.getDeclaredField(field.getName());
targetField.setAccessible(true);
targetField.set(target, value);
} catch (Exception e) {
// 处理异常
}
}
}
}
4. 类分析工具
public class ClassAnalyzer {
public static void analyzeClass(Class<?> clazz) {
System.out.println("=== 分析类: " + clazz.getName() + " ===");
// 获取所有字段
Field[] fields = clazz.getDeclaredFields();
System.out.println("字段信息:");
for (Field field : fields) {
System.out.printf(" %s %s %s%n",
Modifier.toString(field.getModifiers()),
field.getType().getSimpleName(),
field.getName());
}
// 获取所有方法
Method[] methods = clazz.getDeclaredMethods();
System.out.println("方法信息:");
for (Method method : methods) {
System.out.printf(" %s %s %s(%s)%n",
Modifier.toString(method.getModifiers()),
method.getReturnType().getSimpleName(),
method.getName(),
Arrays.toString(method.getParameterTypes()));
}
}
}
注意事项
优点
- 灵活性:运行时动态操作类和对象
- 通用性:编写通用的框架和工具
- 扩展性:支持插件化架构
- 框架支持:Spring、Hibernate等框架的核心技术
缺点
- 性能开销:比直接调用慢10-100倍
- 安全风险:可以访问私有成员,破坏封装性
- 代码复杂:增加维护难度,降低可读性
- 编译时检查缺失:错误只能在运行时发现
最佳实践
- 缓存反射对象
// 好的做法:缓存Method对象
private static final Method TO_STRING_METHOD;
static {
try {
TO_STRING_METHOD = Object.class.getMethod("toString");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
- 异常处理
try {
Method method = clazz.getMethod("methodName");
method.invoke(obj);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
// 具体的异常处理
logger.error("反射调用失败", e);
}
- 性能考虑
// 在性能敏感的地方避免使用反射
// 或者使用MethodHandle等更高效的替代方案
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(String.class, "length", MethodType.methodType(int.class));
核心要点总结
- 反射本质:JVM提供的运行时类信息访问能力
- 实现原理:基于JVM内存中的类元数据和Native方法
- 性能特点:有缓存优化,但仍比直接调用慢
- 应用场景:框架开发、配置驱动、通用工具
- 使用原则:适度使用,注意性能和安全性
记住:反射是Java的高级特性,虽然强大但要谨慎使用。理解其原理有助于更好地使用和优化反射代码!