Choose Theme

Shortcuts: ⌘ ⇧ P
↑↓ Navigate • Enter Confirm • Esc Close
🖥️System
🌻Latte
🦭Nord
🌺Macchiato
🌿Mocha
🏙Tokyo Night
Reflection in Javalqip

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

什么时候会发生类初始化?

  • 类的主动引用(一定会发生类的初始化)
    • 当虚拟机启动,先初始化 main 方法所在的类;
    • new 一个类的对象;
    • 调用类的静态成员(除了 final 常量)和静态方法;
    • 使用 java.lang.reflect 包的方法对类进行反射调用;
    • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类;
  • 类的被动引用(不会发生类的初始化)
    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化;
    • 当通过子类引用父类的静态变量,不会导致子类初始化;
    • 通过数组定义类引用,不会触发此类的初始化;
    • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
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");    }}
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 工具类,通过字节码生成的方式加快反射速度
  • 相对不安全,破坏了封装性(因为通过反射可以获得私有方法和属性)
作者

GnixAij

发布于

2024-03-25

更新于

2025-11-27

许可协议

评论