Java 反射(Reflection)详细教学

1. 什么是 Java 反射?

Java 反射(Reflection) 是 Java 语言中的一个重要机制,它允许程序在运行时动态获取类的信息,并创建对象、调用方法、访问和修改字段,即使在编译时类的具体类型未知。

反射的作用:

  • 获取类的结构信息(类名、构造方法、字段、方法等)。
  • 动态创建对象(即使没有显式地写 new)。
  • 动态调用对象的方法
  • 访问或修改对象的字段(包括私有字段)。
  • 用于开发通用框架,如 Spring、MyBatis、Hibernate 等

2. 反射的核心类

反射主要依赖 java.lang.reflect 包中的几个核心类:

作用
Class<?> 代表 Java 中的类或接口(每个类都有一个 Class 对象)。
Constructor<T> 代表类的构造方法,可以用来创建对象。
Method 代表类的方法,可以用来调用方法。
Field 代表类的字段(成员变量),可以用来获取或修改字段的值。

3. 获取 Class 对象

在 Java 反射机制中,所有的类都会有一个 Class 对象,我们可以通过以下三种方式获取它。

方式 1:使用 Class.forName()

1
2
Class<?> clazz = Class.forName("java.util.ArrayList");
System.out.println(clazz.getName()); // 输出 java.util.ArrayList

适用于: 知道类的全限定名(包名+类名),常用于动态加载类


方式 2:使用 类名.class

1
2
Class<?> clazz = ArrayList.class;
System.out.println(clazz.getName()); // 输出 java.util.ArrayList

适用于: 已知类,但不需要创建实例。


方式 3:使用 对象.getClass()

1
2
3
List<String> list = new ArrayList<>();
Class<?> clazz = list.getClass();
System.out.println(clazz.getName()); // 输出 java.util.ArrayList

适用于: 已有对象,想获取它的类信息。


4. 获取类的信息

反射可以获取类的基本信息,如类名、包名、父类、接口等

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.lang.reflect.Modifier;

public class ReflectionTest {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> clazz = Class.forName("java.util.ArrayList");

System.out.println("类名:" + clazz.getSimpleName());
System.out.println("完整类名:" + clazz.getName());
System.out.println("包名:" + clazz.getPackage().getName());

// 获取父类
System.out.println("父类:" + clazz.getSuperclass().getName());

// 获取实现的接口
System.out.println("实现的接口:");
for (Class<?> i : clazz.getInterfaces()) {
System.out.println(i.getName());
}

// 获取类的修饰符
int modifiers = clazz.getModifiers();
System.out.println("是否为 public:" + Modifier.isPublic(modifiers));
}
}

输出:

1
2
3
4
5
6
7
8
9
10
类名:ArrayList
完整类名:java.util.ArrayList
包名:java.util
父类:java.util.AbstractList
实现的接口:
java.util.List
java.util.RandomAccess
java.lang.Cloneable
java.io.Serializable
是否为 public:true

5. 通过反射创建对象

反射可以动态创建对象,使用 newInstance()Constructor 类。

方式 1:使用 Class.newInstance()

1
2
3
Class<?> clazz = Class.forName("java.util.ArrayList");
Object obj = clazz.getDeclaredConstructor().newInstance();
System.out.println("创建的对象:" + obj);

适用于: 需要创建无参构造方法的实例。


方式 2:使用 Constructor.newInstance()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.lang.reflect.Constructor;

public class ReflectionConstructorTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = String.class;

// 获取 String(String original) 构造方法
Constructor<?> constructor = clazz.getConstructor(String.class);

// 通过反射创建对象
Object obj = constructor.newInstance("Hello Reflection");
System.out.println(obj); // 输出 Hello Reflection
}
}

适用于: 需要调用有参构造创建对象。


6. 通过反射访问字段

反射可以访问和修改私有或公共字段(变量)。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.lang.reflect.Field;

class Person {
private String name = "默认名字";
}

public class ReflectionFieldTest {
public static void main(String[] args) throws Exception {
Person person = new Person();
Class<?> clazz = person.getClass();

// 获取 private 字段
Field field = clazz.getDeclaredField("name");

// 允许访问私有字段
field.setAccessible(true);

// 获取字段值
System.out.println("原始值:" + field.get(person));

// 修改字段值
field.set(person, "张三");
System.out.println("修改后的值:" + field.get(person));
}
}

输出:

1
2
原始值:默认名字
修改后的值:张三

注意: setAccessible(true) 允许访问私有字段。


7. 通过反射调用方法

可以使用 Method.invoke() 动态调用方法,包括私有方法。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.lang.reflect.Method;

class Person {
private void sayHello(String name) {
System.out.println("你好, " + name);
}
}

public class ReflectionMethodTest {
public static void main(String[] args) throws Exception {
Person person = new Person();
Class<?> clazz = person.getClass();

// 获取私有方法
Method method = clazz.getDeclaredMethod("sayHello", String.class);

// 允许访问私有方法
method.setAccessible(true);

// 调用方法
method.invoke(person, "李四");
}
}

输出:

1
你好, 李四

8. 反射与 ClassLoader

反射可以动态加载类,配合 ClassLoader 进行热加载,常用于插件机制和框架开发。

示例

1
2
3
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> clazz = classLoader.loadClass("java.util.Date");
System.out.println(clazz.getName()); // 输出 java.util.Date

9. 反射的应用场景

  1. 框架开发

    (如 Spring、MyBatis):

    • 反射用于依赖注入,如 Spring@Autowired
    • 动态代理(AOP)基于反射实现。
  2. JVM 调试与工具

    • 反射用于开发 IDE 插件,如 IntelliJ IDEA 插件。
  3. 动态加载类

    • 适用于插件系统,例如 Minecraft 插件、Tomcat 动态加载 Web 应用。
  4. 序列化与反序列化

    • JSON 库(如 Jackson、Gson)基于反射解析对象。

10. 总结

🔹 反射允许运行时获取类信息、创建对象、调用方法、访问字段
🔹 常用类包括 Class<?>Constructor<?>MethodField
🔹 反射用于框架开发、动态代理、插件系统
🔹 反射会降低性能,应谨慎使用,必要时使用 缓存 机制优化。

🚀 掌握反射,你就掌握了 Java 的动态魔法!