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
JAVA
// 获取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
使用反射获取构造器并创建实例
JAVA
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
JAVA
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"); }}
JAVA
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"); } }}
SHELL
$ java -versionopenjdk version "22.0.2" 2024-07-16OpenJDK 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.EmployeeConstructor Parameter Count: 0Constructor Name: reflection.EmployeeConstructor Parameter Count: 2Constructor's parameter: int arg0Constructor's parameter: double arg1New instance using reflectionEmployee [id=1, salary=2.3]======== Constructor API END ================ Method API ========Number of methods: 4Method name: toStringMethod return type: java.lang.StringMethod parameter count: 0Method name: getIdMethod return type: intMethod parameter count: 0Method name: getSalaryMethod return type: doubleMethod parameter count: 0Method name: showMethod return type: voidMethod parameter count: 0Invoke method using reflectionMethod 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