Skip to content

Java 的反射机制

通过类(java.lang.Class)对其实例的各种动态操作,称为反射

反射操作的 API

java
public class ReflectionTest {

    static class Person {
        public Person() {
        }

        public Person(String name) {
            this.name = name;
        }

        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    static class Student extends Person {

        public Student(String name, int age) {
            super(name);
            this.age = age;
        }

        public Student(String name, int age, String title) {
            super(name);
            this.age = age;
            this.title = title;
        }

        private int age;

        private String title;

        public static void hi() {
            System.out.println("hi");
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "name='" + super.name + '\'' +
                    ", age=" + age +
                    ", title='" + title + '\'' +
                    '}';
        }
    }

    @Test
    public void testCreateClass() throws ClassNotFoundException {
        // 1. 通过全类路径从 JVM 中查找
        Class<?> clazz1 = Class.forName("com.demo.base.reflaction.ReflectionTest.Student");
        // 2. 通过类定义获取
        Class<Student> clazz2 = Student.class;
        // 3. 通过对象获取
        Student student = new Student("me", 18);
        Class<? extends Student> clazz3 = student.getClass();

    }

    @SneakyThrows
    @Test
    public void testClassApi() {
        Class<Student> clazz = Student.class;
        // 1. 不带 Declared 的方法支持获取所有非 private 的,即继承、Public 的,但不包括 Private 修饰的
        // 2. 带 Declared 的方法支持获取 Class 自己声明的所有的,即 Public、Protected、默认(包)和 Private 的,但不包括继承的
        // 带 s 结尾的方法为获取所有

        // 操作构造
        Constructor<Student> constructor = clazz.getConstructor(String.class, int.class, String.class);
        Constructor<?>[] constructors = clazz.getConstructors();
        Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
        Student student = constructor.newInstance("one", 15, "cto");
        System.out.println("creat instance by reflection: " + student);

        // 操作属性
        Field nameField = clazz.getDeclaredField("title");
        Field[] fields = clazz.getFields();
        Field[] declaredFields = clazz.getDeclaredFields();
        // 设置可访问私有属性
        nameField.setAccessible(true);
        System.out.println("query title field: " + nameField.get(student));

        // 操作方法
        Method getTitleMethod = clazz.getMethod("getTitle");
        Method[] methods = clazz.getMethods();
        Method[] declaredMethods = clazz.getDeclaredMethods();
        Object invoke = getTitleMethod.invoke(student);
        System.out.println("invoke getTitle method: " + invoke);

        // 调用静态方法,对象传 null
        Method hiMethod = clazz.getMethod("hi");
        hiMethod.invoke(null);
    }
}

使用场景

  1. 简单工厂设计模式中通过传入类名创建实例
  2. JDK 动态代理

特点

优点:动态编译,使用灵活

缺点: 相对执行效率低,不推荐频繁调用的地方使用(比如将 model 转为 DTO 时)

效率低的原因:

  1. 动态编译,编译器不能提前优化
  2. 调用时通过反射查找属性、方法的过程慢
  3. 各种检查:安全检查、参数检查、可见性检查
  4. 方法调用时需要做参数的封装和解封
  5. 不能进行方法内联(方法内联:JVM 在运行时将调用次数达到一定阈值的方法调用替换为方法体本身)