• Java基础——反射


    1 概述

    Java反射提供了一种动态获取类信息及动态修改运行规则的机制。
    反射指运行时获取一个类中的信息,并控制其行为。运行时可以获取构造器对象Constructor,可以获取成员变量对象Field,可以获取方法对象Method

    2 获取类对象的方式

    获取Class对象有三种方式

    public class Test {
        public static void main(String[] args) throws ClassNotFoundException {
            // Class类中的一个静态方法forName
            Class clazz1 = Class.forName("com.xy.day01.Student");
            System.out.println(clazz1);
    
            // 通过类名.class
            Class clazz2 = Student.class;
            System.out.println(clazz2);
    
            // 通过对象的getClass方法
            Student student = new Student();
            Class clazz3 = student.getClass();
            System.out.println(clazz3);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    输出

    class com.xy.day01.Student
    class com.xy.day01.Student
    class com.xy.day01.Student
    
    • 1
    • 2
    • 3

    Class对象是对象加载之后自动创建的一个对象,在堆中。每个类加载后,虚拟机都会为其创建一个对象在堆中,有且只有一个。

    2 反射构造方法

    涉及到的api

    1. Constructor getConstructor(Class... parameterTypes)
       根据参数匹配获取某个构造器,只能拿public修饰的构造器,几乎不用!
    2. Constructor getDeclaredConstructor(Class... parameterTypes)
       根据参数匹配获取某个构造器,只要申明就可以定位,不关心权限修饰符,建议使用!
    3. Constructor[] getConstructors()
       获取所有的构造器,只能拿public修饰的构造器。几乎不用!!太弱了!
    4. Constructor[] getDeclaredConstructors()
       获取所有申明的构造器,只要你写我就能拿到,无所谓权限。建议使用!!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    测试类

    public class Student {
        int age;
        String name;
    
        private Student() {
            System.out.println("无参构造执行了");
        }
    
        public Student(int age, String name) {
            System.out.println("有参构造执行了");
            this.age = age;
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        
    	@Override
        public String toString() {
            return "Student{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    • 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

    2.1 获取所有public构造器

    @Test
    public void getConstructors() {
        Class<Student> clazz = Student.class;
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输出

    com.xy.day02.Student ===> 2
    
    • 1

    该方法只能获取所有的public类型的构造器

    2.2 获取所有构造器

    @Test
    public void getDeclaredConstructors() {
        Class<Student> clazz = Student.class;
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输出

    com.xy.day02.Student ===> 0
    com.xy.day02.Student ===> 2
    
    • 1
    • 2

    该方法能够获取所有的构造器,包括private类型的构造器

    2.3 获取单个public构造器

    @Test
    public void getConstructor() throws NoSuchMethodException {
        Class<Student> clazz = Student.class;
        Constructor constructor = clazz.getConstructor();
        // Constructor constructor = clazz.getConstructor(int.class, String.class);
        System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    报异常
    在这里插入图片描述
    因为无参构造是private的,该方法只能获取public类型的构造器,由于获取不到对应构造器,所以报异常。

    @Test
    public void getConstructor() throws NoSuchMethodException {
        Class<Student> clazz = Student.class;
        Constructor constructor = clazz.getConstructor();
        // Constructor constructor = clazz.getConstructor(int.class, String.class);
        System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    输出

    com.xy.day02.Student ===> 2
    
    • 1

    通过传入特定的class对象来获取对应的构造器

    2.4 获取单个构造器

    @Test
    public void getDeclaredConstructor() throws NoSuchMethodException {
        Class<Student> clazz = Student.class;
        Constructor<Student> constructor1 = clazz.getDeclaredConstructor();
        Constructor<Student> constructor2 = clazz.getDeclaredConstructor(int.class, String.class);
        System.out.println(constructor1.getName() + " ===> " + constructor1.getParameterCount());
        System.out.println(constructor2.getName() + " ===> " + constructor2.getParameterCount());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输出

    com.xy.day02.Student ===> 0
    com.xy.day02.Student ===> 2
    
    • 1
    • 2

    通过该方法可以获取包括private类型的构造器。

    3 构造器的使用

    @Test
    public void useConstructor() throws Exception {
        Class<Student> clazz = Student.class;
        Constructor<Student> constructor = clazz.getDeclaredConstructor();
        System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());
    
        Student s = (Student) constructor.newInstance();
        System.out.println(s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    反射无参构造器,并创建对象,报异常
    在这里插入图片描述
    由于无参构造方法是private的,所以会报异常
    可以通过setAccessible方法设置。

    @Test
    public void useConstructor() throws Exception {
        Class<Student> clazz = Student.class;
        Constructor<Student> constructor = clazz.getDeclaredConstructor();
        System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());
    
        constructor.setAccessible(true);
    
        Student s = (Student) constructor.newInstance();
        System.out.println(s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    输出

    com.xy.day02.Student ===> 0
    无参构造执行了
    Student{age=0, name='null'}
    
    • 1
    • 2
    • 3

    setAccessible设置为true,表示可以暴力进入,无视权限控制。false为默认,保留权限控制。

    @Test
    public void useConstructor() throws Exception {
        Class<Student> clazz = Student.class;
        Constructor<Student> constructor = clazz.getDeclaredConstructor(int.class, String.class);
        System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());
        Student s = (Student) constructor.newInstance(12, "Bob");
        System.out.println(s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输出

    com.xy.day02.Student ===> 2
    有参构造执行了
    Student{age=12, name='Bob'}
    
    • 1
    • 2
    • 3

    4 反射字段

    反射字段相关的api

    1Field getField(String name);
           根据成员变量名获得对应Field对象,只能获得public修饰
    2.Field getDeclaredField(String name);
           根据成员变量名获得对应Field对象,只要申明了就可以得到
    3.Field[] getFields();
           获得所有的成员变量对应的Field对象,只能获得public4.Field[] getDeclaredFields();
           获得所有的成员变量对应的Field对象,只要申明了就可以得到
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.1 获取所有public字段

    @Test
    public void getFields() {
        Class clazz = Student.class;
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field.getName() + "===>" + field.getType());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    返回为空,因为所有字段都是default的

    4.2 获取所有字段

    @Test
    public void getDeclaredFields() {
        Class clazz = Student.class;
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName() + "===>" + field.getType());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输出

    age===>int
    name===>class java.lang.String
    
    • 1
    • 2

    4.3 获取单个public字段

    @Test
    public void getField() throws NoSuchFieldException {
        Class clazz = Student.class;
        Field field = clazz.getField("age");
        System.out.println(field.getName() + "===>" + field.getType());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    抛异常,因为该字段不是public的
    在这里插入图片描述

    4.4 获取单个字段

    @Test
    public void getDeclaredField() throws NoSuchFieldException {
        Class clazz = Student.class;
        Field field = clazz.getDeclaredField("age");
        System.out.println(field.getName() + "===>" + field.getType());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    输出

    age===>int
    
    • 1

    5 字段的操作

    常用api有

    void set(Object obj, Object value):给对象注入某个成员变量数据
    Object get(Object obj):获取对象的成员变量的值。
    void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。
    Class getType(); 获取属性的类型,返回Class对象。
    String getName(); 获取属性的名称。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    代码示例:

    @Test
    public void useField() throws Exception {
        Class clazz = Student.class;
        Field field = clazz.getDeclaredField("age");
        Constructor<Student> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
    
        field.setAccessible(true);
    
        Student s = (Student) constructor.newInstance();
        field.set(s, 23);
        System.out.println(s);
    
        int age = (int) field.get(s);
        System.out.println(age);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    输出

    无参构造执行了
    Student{age=23, name='null'}
    23
    
    • 1
    • 2
    • 3

    6 反射方法

    常用api

    1Method getMethod(String name,Class...args);
        根据方法名和参数类型获得对应的方法对象,只能获得public2Method getDeclaredMethod(String name,Class...args);
        根据方法名和参数类型获得对应的方法对象,包括private3Method[] getMethods();
        获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的
    4Method[] getDeclaredMethods();
       获得类中的所有成员方法对象,返回数组,只获得本类申明的方法。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    测试类

    public class Dog {
        private String name ;
        public Dog(){
        }
    
        public Dog(String name) {
            this.name = name;
        }
    
        public void run(){
            System.out.println("狗跑的贼快~~");
        }
    
        private void eat(){
            System.out.println("狗吃骨头");
        }
    
        private String eat(String name){
            System.out.println("狗吃" + name);
            return "吃的很开心!";
        }
    
        public static void inAddr(){
            System.out.println("在黑马学习Java!");
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    }
    
    • 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

    6.1 获取所有方法

    @Test
    public void getDeclaredMethods(){
        Class clazz = Dog.class;
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName() +" 返回值类型:" + method.getReturnType() + " 参数个数:" + method.getParameterCount());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输出

    getName 返回值类型:class java.lang.String 参数个数:0
    run 返回值类型:void 参数个数:0
    setName 返回值类型:void 参数个数:1
    inAddr 返回值类型:void 参数个数:0
    eat 返回值类型:void 参数个数:0
    eat 返回值类型:class java.lang.String 参数个数:1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    6.2 获取单个方法并执行

    @Test
    public void getDeclardMethod() throws Exception {
        Class clazz = Dog.class;
        Method m = clazz.getDeclaredMethod("eat");
        Method m2 = clazz.getDeclaredMethod("eat", String.class);
    
        m.setAccessible(true);
        m2.setAccessible(true);
    
        Dog d = new Dog();
        
        Object result = m.invoke(d);
        System.out.println(result);
    
        Object result2 = m2.invoke(d, "骨头");
        System.out.println(result2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    输出

    狗吃骨头
    null
    狗吃骨头
    吃的很开心!
    
    • 1
    • 2
    • 3
    • 4

    7 总结

    反射提供了一种运行时获取类信息并操作的机制,增大了Java代码的灵活性,但是同时也破坏了封装特性,因为可以反射获取private类型的数据。
    反射主要涉及Class、Field、Constructor、Method这几个对象的使用。

  • 相关阅读:
    C++STL【string】下模拟实现string
    Python的NumPy库(二)进阶用法
    二维偏序问题
    题目:2729.判断一个数是否迷人
    only id(String) method calls allowed in plugins {} script block
    直播录屏没有声音?解决方案来了!
    DCL 单例模式设计为什么需要 volatile 修饰实例对象
    matlab用dde23求解带有固定时滞的时滞微分方程
    如何使用LoRA和PEFT微调 Mistral 7B 模型
    图片如何转换成PDF格式?教你一招快速转换
  • 原文地址:https://blog.csdn.net/weixin_49274713/article/details/132777746