深入理解 Java 反射:原理、使用场景与性能优化(超详细)
·
在 Java 面试中,几乎所有面试官都会问一个问题:
你了解 Java 反射吗?
很多人会回答:
Class.forName()
newInstance()
但如果继续追问:
- 反射的原理是什么?
- 为什么框架大量使用反射?
- 反射为什么慢?
- Spring 是怎么用反射的?
很多人就答不上来了。
这篇文章会带你 彻底理解 Java 反射。
一、什么是反射
Java 反射(Reflection)是指:
在程序运行时动态获取类的信息,并操作类或对象的能力。
简单理解:
正常代码是:
User user = new User();
user.setName("Tom");
而反射可以做到:
Class<?> clazz = Class.forName("User");
Object obj = clazz.newInstance();
Method method = clazz.getMethod("setName", String.class);
method.invoke(obj, "Tom");
也就是说:
程序可以在运行时操作类。
二、反射的核心类
Java 反射主要依赖以下几个类:
| 类 | 作用 |
|---|---|
| Class | 类的元数据 |
| Method | 方法对象 |
| Field | 成员变量 |
| Constructor | 构造方法 |
它们都在:
java.lang.reflect
三、获取 Class 对象的三种方式
反射的第一步是:
获取 Class 对象。
方式1:Class.forName
Class<?> clazz = Class.forName("com.demo.User");
特点:
- 常用于 框架加载类
例如:
DriverManager.registerDriver(
(Driver) Class.forName(driverClass).newInstance()
);
方式2:类名.class
Class<User> clazz = User.class;
特点:
- 编译期已知类型
方式3:对象.getClass()
User user = new User();
Class<?> clazz = user.getClass();
特点:
- 运行时获取
四、通过反射创建对象
普通创建对象:
User user = new User();
反射创建:
Class<?> clazz = Class.forName("User");
Object obj = clazz.getDeclaredConstructor().newInstance();
注意:
newInstance()
已经被标记为 过时。
推荐:
getDeclaredConstructor().newInstance()
五、通过反射操作属性
假设有一个类:
public class User {
private String name;
}
通过反射修改属性:
Class<?> clazz = User.class;
Object obj = clazz.getDeclaredConstructor().newInstance();
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(obj, "Tom");
获取属性:
String value = (String) field.get(obj);
六、通过反射调用方法
假设:
public class User {
public void sayHello() {
System.out.println("hello");
}
}
反射调用:
Class<?> clazz = User.class;
Object obj = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getMethod("sayHello");
method.invoke(obj);
七、获取类的所有信息
反射可以获取:
- 属性
- 方法
- 构造函数
- 注解
- 接口
例如:
获取所有字段
Field[] fields = clazz.getDeclaredFields();
获取所有方法
Method[] methods = clazz.getDeclaredMethods();
获取构造方法
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
八、反射的典型使用场景
很多人觉得反射只是面试题,其实不是。
几乎所有 Java 框架都在使用反射。
例如:
- Spring
- MyBatis
- Hibernate
- Jackson
1 Spring IOC
Spring 创建 Bean 的时候:
Class<?> clazz = Class.forName(beanClass);
Object bean = clazz.getDeclaredConstructor().newInstance();
然后再通过反射:
field.set(bean, value);
完成 依赖注入。
2 MyBatis 映射对象
SQL 查询:
select id,name from user
MyBatis 会通过反射:
user.setId(resultSet.getInt("id"));
user.setName(resultSet.getString("name"));
3 JSON 解析
例如:
{
"name":"Tom"
}
Jackson 会:
1 解析 JSON
2 通过反射调用 setter
九、反射为什么慢
反射慢的原因主要有三个:
1 破坏 JVM 优化
普通代码:
user.setName("Tom");
JVM 可以:
- JIT 编译
- 内联优化
反射:
method.invoke()
JVM 很难优化。
2 权限检查
反射会做:
SecurityManager
检查。
如果:
setAccessible(true)
可以减少部分开销。
3 方法解析
反射调用需要:
Method -> 方法查找 -> 执行
而不是直接调用。
十、反射性能优化
如果必须使用反射,可以优化:
1 缓存 Method
不要每次获取:
clazz.getMethod()
建议:
Map<String, Method> cache
2 使用 MethodHandle
JDK7 引入:
MethodHandle
性能接近直接调用。
示例:
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh =
lookup.findVirtual(User.class,"sayHello",
MethodType.methodType(void.class));
mh.invoke(user);
3 使用字节码生成
例如:
- CGLIB
- ByteBuddy
很多框架都会这样做。
十一、反射的安全问题
反射可以破坏封装。
例如:
field.setAccessible(true);
可以访问:
private
字段。
所以很多安全框架会限制:
SecurityManager
十二、面试常见问题
1 什么是反射?
运行时动态获取类信息并操作对象。
2 反射的优缺点
优点:
- 灵活
- 动态
缺点:
- 性能较低
- 破坏封装
3 反射的应用场景
- Spring IOC
- MyBatis
- JSON 序列化
- 动态代理
4 反射为什么慢?
- JVM 优化失效
- 权限检查
- 方法解析
十三、总结
Java 反射本质是:
在运行时动态操作类。
核心能力:
获取类
创建对象
访问字段
调用方法
反射让 Java 具备:
动态编程能力。
这也是为什么:
Spring
MyBatis
Hibernate
这些框架都大量使用反射。
更多推荐




所有评论(0)