在 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

这些框架都大量使用反射。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐