Reflection in Java
Reflection
反射(Reflection)允许程序在运行时检查和修改自身的结构和行为。通过反射,程序可以动态地获取类的详细信息,包括类的成员变量、方法、构造器等,并可以在运行时调用这些成员。
这种能力使得 Java 在某种程度上具有了动态语言的特性。Spring/Spring Boot、MyBatis 等框架以及 Java 注解均大量应用了反射机制。此外,在使用 IDE 进行开发时,当输入一个对象或类后按下.
,IDE 会自动列出该对象或类的所有可用属性和方法。这一功能背后也是通过反射机制实现的
java.lang.reflect
包中提供了 reflection API,使用反射,可以在 runtime 期间进行以下操作:
- 判断任意一个对象所属的类
- 构造任意一个类的对象
- 判断任意一个类所具有的成员变量和方法
- 获取泛型信息
- 调用任意一个对象的成员变量和方法
- 处理注解
- 生成动态代理
反射相关 API
java.lang.Class
: 代表一个类java.lang.reflect.Method
: 代表类的方法java.lang.reflect.Field
: 代表类的成员变量java.lang.reflect.Constructor
: 代表类的构造器
Runtime Class
java.lang.Class<T>
表示正在运行的 Java 应用程序中的类和接口
当 Java 程序启动时,类加载器(class loader)负责将 .class 文件中的字节码加载到内存中。
加载过程中,虚拟机会为被使用的 class 创建一个类型为 java.lang.Class
的实例(Instance),用于表示该类,一般称这个类为 runtime class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 获取Class实例的4种方法
public void getClazz() throws ClassNotFoundException {
// 方式一:Employee.class
Class c1 = Employee.class;
// 方式二:emploee.getClass()
Employee p1 = new Employee();
Class c2 = p1.getClass();
// 方式三:Class.forName(String classPath);传入类的全路径获取
Class c3 = Class.forName("reflection.Employee");
// 方式四:使用类的加载器:ClassLoader ( 了解 )
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class c4 = classLoader.loadClass("reflection.Employee");
System.out.println(c4); // class reflection.Employee
System.out.println(c1 == c4); // true
}
Syntax
使用反射获取构造器并创建实例
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
Class<?> personClass = Class.forName("Person");
Constructor<?> constructor = personClass.getConstructor(String.class, int.class);
Object personInstance0 = constructor.newInstance("Alice", 30);
Object personInstance1 = constructor.newInstance(new Object[]{"Alice", 30});
Usage Example
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
// Returns the Class object associated with the class or interface with the
// given string name.
Class<?> clazz = Class.forName("reflection.Employee");
printConstructors(clazz);
printMethods(clazz);
printClasses(clazz);
printAnnotations(clazz);
}
private static void printConstructors(Class<?> clazz) {
System.out.println("======== Constructor API ========");
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("Constructor Name: " + constructor.getName());
System.out.println("Constructor Parameter Count: " + constructor.getParameterCount());
Parameter[] parameters = constructor.getParameters();
for (Parameter parameter : parameters) {
System.out.println("Constructor's parameter: " + parameter);
}
}
// 通过反射创建对象
System.out.println("New instance using reflection");
Constructor<?> cons;
try {
cons = clazz.getConstructor(int.class,double.class);
Employee employee = (Employee) cons.newInstance(1, 2.3);
System.out.println(employee);
} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("======== Constructor API END ========\n");
}
private static void printMethods(Class<?> c) {
System.out.println("======== Method API ========");
Method[] methods = c.getDeclaredMethods();
System.out.println("Number of methods: " + methods.length);
for (Method method : methods) {
System.out.println("Method name: " + method.getName());
System.out.println("Method return type: " + method.getReturnType().getName());
System.out.println("Method parameter count: " + method.getParameterCount());
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
System.out.println("Method's Parameter: " + parameter);
}
}
// Invoke method
System.out.println("Invoke method using reflection");
try {
Method method = c.getMethod("getSalary");
System.out.println("Method return value: " + method.invoke(new Employee()));
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("======== Method API END ========\n");
}
private static void printClasses(Class<?> c) {
System.out.println("======== Class API ========");
Class<?>[] classes = c.getDeclaredClasses();
for (Class<?> class1 : classes) {
System.out.println("Class: " + class1.getName());
}
System.out.println("======== Class API END ========\n");
}
private static void printAnnotations(Class<?> c) {
System.out.println("======== Annotation API ========");
// 获取类上的注解
Annotation[] classAnnotations = c.getDeclaredAnnotations();
for (Annotation annotation : classAnnotations) {
System.out.println("Class Annotation: " + annotation);
}
// 获取方法上的注解
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
Annotation[] methodAnnotations = method.getDeclaredAnnotations();
for (Annotation annotation : methodAnnotations) {
System.out.println("Method Annotation: " + annotation);
}
}
// 获取构造器上的注解
Constructor<?>[] constructors = c.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
Annotation[] constructorAnnotations = constructor.getDeclaredAnnotations();
for (Annotation annotation : constructorAnnotations) {
System.out.println("Constructor Annotation: " + annotation);
}
}
System.out.println("======== Annotation API END ========\n");
}
}
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package reflection;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MyClassAnnotation {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyMethodAnnotation {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
@interface MyConstructorAnnotation {
String value();
}
@MyClassAnnotation("This is a class annotation")
public class Employee {
private int id;
private double salary;
public Employee() {
}
@MyConstructorAnnotation("This is a constructor annotation")
public Employee(int eid, double esal) {
this.id = eid;
this.salary = esal;
}
@MyMethodAnnotation("This is a method annotation")
public void show() {
System.out.println("Hello");
}
public int getId() {
return id;
}
public double getSalary() {
return salary;
}
@Override
public String toString() {
return "Employee [id=" + id + ", salary=" + salary + "]";
}
class Inner {
public void print() {
System.out.println("Inner class");
}
}
}
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
$ java -version
openjdk version "22.0.2" 2024-07-16
OpenJDK Runtime Environment (build 22.0.2+9-70)
OpenJDK 64-Bit Server VM (build 22.0.2+9-70, mixed mode, sharing)
$ java ./Main.java
======== Constructor API ========
Constructor Name: reflection.Employee
Constructor Parameter Count: 0
Constructor Name: reflection.Employee
Constructor Parameter Count: 2
Constructor's parameter: int arg0
Constructor's parameter: double arg1
New instance using reflection
Employee [id=1, salary=2.3]
======== Constructor API END ========
======== Method API ========
Number of methods: 4
Method name: toString
Method return type: java.lang.String
Method parameter count: 0
Method name: getId
Method return type: int
Method parameter count: 0
Method name: getSalary
Method return type: double
Method parameter count: 0
Method name: show
Method return type: void
Method parameter count: 0
Invoke method using reflection
Method return value: 0.0
======== Method API END ========
======== Class API ========
Class: reflection.Employee$Inner
======== Class API END ========
======== Annotation API ========
Class Annotation: @reflection.MyClassAnnotation("This is a class annotation")
Method Annotation: @reflection.MyMethodAnnotation("This is a method annotation")
Constructor Annotation: @reflection.MyConstructorAnnotation("This is a constructor annotation")
======== Annotation API END ========
八股
反射机制的优缺点
优点:
- 能够运行时动态获取类的实例,提高灵活性;
- 与动态编译结合
缺点:
- 使用反射性能较低,需要解析字节码,将内存中的对象进行解析。 解决方案:
- 通过 setAccessible(true)关闭 JDK 的安全检查来提升反射速度;
- 多次创建一个类的实例时,有缓存会快很多
- ReflectASM 工具类,通过字节码生成的方式加快反射速度
- 相对不安全,破坏了封装性(因为通过反射可以获得私有方法和属性)
Reflection in Java