• 【檀越剑指大厂--反射】反射总结


    一.反射概念

    Java 的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为 Java 语言的反射机制。反射被视为动态语言的关键。

    • java.lang.reflect AccessibleObject 类
    • java.lang.reflect Array 类
    • java.lang.reflect Constructor 类
    • java.lang.reflect Field 类
    • java.lang.reflect Method 类
    • java.lang.reflect Modifier 类
    • java.lang.reflect Proxy 类

    Class 类实例表示正在运行的 Java 应用程序中的类和接口。Class 是普通类、接口、枚举类、数组等的抽象,即它们的类型就是 Class,它们是 Class 的实例。

    二.使用反射

    1.获取 Class

    //第一种
    public class Basic_Reflect_01_getName {
        public static void main(String[] args) {
            //1.获取并输出类的名称
            Class mClass = SonClass.class;
            System.out.println("类的名称:" + mClass.getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    public class Basic_Reflect_01_getName_02 {
        public static void main(String[] args) {
            //1.获取并输出类的名称
            SonClass x = new SonClass();
            Class mClass = x.getClass();
            System.out.println("类的名称:" + mClass.getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    public class Basic_Reflect_01_getName_03 {
        public static void main(String[] args) throws ClassNotFoundException {
            //1.获取并输出类的名称
            Class mClass = Class.forName("com.xiaofei.antbasic.basic_reflect.SonClass");
            System.out.println("类的名称:" + mClass.getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.常见方法

    I、getName()

    一个 Class 对象描述了一个特定类的属性,Class 类中最常用的方法 getName 以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。

    II、newInstance()

    Class 还有一个有用的方法可以为类创建一个实例,这个方法叫做 newInstance()。例如:
    x.getClass.newInstance(),创建了一个同 x 一样类型的新实例。newInstance()方法调用默认构造器(无参数构造器)初始化新建对象。

    III、getClassLoader()

    返回该类的类加载器。

    VI、getComponentType()

    返回表示数组组件类型的 Class。

    V、getSuperclass()

    返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。

    VI、isArray()

    判定此 Class 对象是否表示一个数组类。

    4.getFields 和 getDeclaredFields 的区别?

    getFields:通过反射获取类的所有变量 获取所有 public 访问权限的变量

    getDeclaredFields:获取所有本类声明的变量(不限制访问权限)

    getMethods 和 getDeclaredMethods 同理

    //变量
    //访问权限
    //类型
    //名称
    public class Basic_Reflect_02_getFields_01 {
        public static void main(String[] args) {
            Class mClass = SonClass.class;
            //获取所有 public 访问权限的变量
            // 包括本类声明的和从父类继承的
            Field[] fields = mClass.getFields();
            //3. 遍历变量并输出变量信息
            for (Field field : fields) {
                //获取访问权限并输出
                int modifiers = field.getModifiers();
                System.out.print(Modifier.toString(modifiers) + " ");
                //输出变量的类型及变量名
                System.out.println(field.getType().getName()
                        + " " + field.getName());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    5.通过 getMethods 可以获取哪些信息?

    //修饰符
    //返回值类型
    //方法参数
    //异常
    public class Basic_Reflect_03_getMethods_01 {
        public static void main(String[] args) {
            Class mClass = SonClass.class;
            //获取所有 public 访问权限的方法
            //包括自己声明和从父类继承的
            Method[] mMethods = mClass.getMethods();
            //3.遍历所有方法
            for (Method method : mMethods) {
                //获取并输出方法的访问权限(Modifiers:修饰符)
                int modifiers = method.getModifiers();
                System.out.print(Modifier.toString(modifiers) + " ");
                //获取并输出方法的返回值类型
                Class returnType = method.getReturnType();
                System.out.print(returnType.getName() + " "
                        + method.getName() + "( ");
                //获取并输出方法的所有参数
                Parameter[] parameters = method.getParameters();
                for (Parameter parameter :
                        parameters) {
                    System.out.print(parameter.getType().getName()
                            + " " + parameter.getName() + ",");
                }
                //获取并输出方法抛出的异常
                Class[] exceptionTypes = method.getExceptionTypes();
                if (exceptionTypes.length == 0) {
                    System.out.println(" )");
                } else {
                    for (Class c : exceptionTypes) {
                        System.out.println(" ) throws "
                                + c.getName());
                    }
                }
            }
        }
    }
    
    • 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

    6.访问私有方法?

    privateMethod.setAccessible(true);

    public class Basic_Reflect_04_getPrivateMethod {
        public static void main(String[] args) throws Exception {
            //1. 获取 Class 类实例
            TestClass testClass = new TestClass();
            Class mClass = testClass.getClass();
            //2. 获取私有方法
            //第一个参数为要获取的私有方法的名称
            //第二个为要获取方法的参数的类型,参数为 Class...,没有参数就是null
            //方法参数也可这么写 :new Class[]{String.class , int.class}
            Method privateMethod = mClass.getDeclaredMethod("privateMethod", String.class, int.class);
            //3. 开始操作方法
            if (privateMethod != null) {
                //获取私有方法的访问权
                //只是获取访问权,并不是修改实际权限
                privateMethod.setAccessible(true);
                //使用 invoke 反射调用私有方法
                //privateMethod 是获取到的私有方法
                //testClass 要操作的对象
                //后面两个参数传实参
                privateMethod.invoke(testClass, "Java Reflect ", 666);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    7.修改私有变量

    public class Basic_Reflect_05_modifyPrivateFiled {
        public static void main(String[] args) throws Exception {
            //1. 获取 Class 类实例
            TestClass testClass = new TestClass();
            Class mClass = testClass.getClass();
            //2. 获取私有变量
            Field privateField = mClass.getDeclaredField("MSG");
            //3. 操作私有变量
            if (privateField != null) {
                //获取私有变量的访问权
                privateField.setAccessible(true);
                //修改私有变量,并输出以测试
                System.out.println("Before Modify:MSG = " + testClass.getMsg());
                //调用 set(object , value) 修改变量的值
                //privateField 是获取到的私有变量
                //testClass 要操作的对象
                //"Modified" 为要修改成的值
                privateField.set(testClass, "Modified");
                System.out.println("After Modify:MSG = " + testClass.getMsg());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    8.修改常量 final

    • 第一句打印修改前 FINAL_VALUE 的值,没有异议;
    • 第二句打印修改后常量的值,说明 FINAL_VALUE 确实通过反射修改了;
    • 第三句打印通过 getFinalValue() 方法获取的 FINAL_VALUE 的值,但还是初始值,导致修改无效!

    原因 :程序运行时是根据编译后的 .class 来执行的。

    办法:避免在编译时刻被优化

    public class Basic_Reflect_06_modifyFinalFiled {
        public static void main(String[] args) throws Exception {
            //1. 获取 Class 类实例
            TestClass testClass = new TestClass();
            Class mClass = testClass.getClass();
            //2. 获取私有常量
            Field finalField = mClass.getDeclaredField("FINAL_VALUE");
            //3. 修改常量的值
            if (finalField != null) {
                //获取私有常量的访问权
                finalField.setAccessible(true);
                //调用 finalField 的 getter 方法
                //输出 FINAL_VALUE 修改前的值
                System.out.println("Before Modify:FINAL_VALUE = "
                        + finalField.get(testClass));
                //修改私有常量
                finalField.set(testClass, "Modified");
                //调用 finalField 的 getter 方法
                //输出 FINAL_VALUE 修改后的值
                System.out.println("After Modify:FINAL_VALUE = "
                        + finalField.get(testClass));
                //使用对象调用类的 getter 方法
                //获取值并输出
                System.out.println("Actually :FINAL_VALUE = "
                        + testClass.getFinalValue());
            }
        }
    }
    
    • 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
    private final String FINAL_VALUE_000 = null == null ? "FINAL" : null;
    
    • 1

    9.构造函数

    • 先获取getConstructor
    • 再调用newInstance,获取实例
    //无参构造函数
    public class Basic_Reflect_07_getConstructor_01 {
        public static void main(String[] args) throws Exception {
            //第一步:获取到Class对象
            Class personClazz = TestClass.class;
            // 第二步:获取构造方法
            Constructor<TestClass> constructor = personClazz.getConstructor();
            // 第三步:创建对象
            TestClass testClass = constructor.newInstance();
            System.out.println(testClass);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    //有参构造函数
    public class Basic_Reflect_07_getConstructor_02 {
        public static void main(String[] args) throws Exception {
            // 第一步:获取到Class对象
            Class personClazz = Person.class;
            // 第二步:获取构造方法
            Constructor<Person> constructor = personClazz.getConstructor(String.class, Integer.class);
            // 第三步:创建对象
            Person person = constructor.newInstance("张三", 10);
            System.out.println(person);
            System.out.println(person.getName() + ":" + person.getAge());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    三.常见问题

    1.利用反射创建类实例?

    方法一:newInstance

    //首先获取Class对象
    Class clazz=Class.forClass("test.Student");
    //创建对象
    Student stu=(Student)clazz.newInstance();
    
    • 1
    • 2
    • 3
    • 4

    方法二:getConstructor

    //首先创建Class对象
    Class clazz=Class.forClass("test.Student");
    //获取想调用的构造函数
    Constructor constructor=clazz.getConstructor(String.class, int.class);
    //调用Constructor的newInstance()方法
    Student stu=(Student)constructor.newInstance("大王"20);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.调用方法的方式?

    反射要调用类中的方法,需要通过关键方法“invoke()”实现的,方法调用也分为三种:

    • 静态(static)方法调用
    • 普通方法调用
    • 私有方法调用
    //privateMethod 是获取到的方法
    //testClass 要操作的对象
    //后面两个参数传实参
    privateMethod.invoke(testClass, "Java Reflect ", 666);
    
    • 1
    • 2
    • 3
    • 4

    3.new 关键字和 newInstance()方法的区别?

    • newInstance: 弱类型。低效率。只能调用无参构造。 构造器的 newInstance 可以是有参的
    • new: 强类型。相对高效。能调用任何 public 构造。

    4.反射创建数组?

    数组本质上是一个 Class,而在 Class 中存在一个方法用来识别它是否为一个数组。

    反射创建数组是通过 Array.newInstance(T.class,维数) 这个方法。

    第一个参数指定的是数组内的元素类型,后面的是可变参数,表示的是相应维度的数组长度限制。

    //比如,我要创建一个 int [2][3]的数组。
    Int[][]  a=Array.newInstance(Integer.TYPE, 2, 3);
    
    • 1
    • 2

    5.Spring创建bean

    Spring组件即通过反射使用无参构造方法创建bean:

    <bean id="person" class="com.company.demo2.Person">bean>
    
    • 1

    Spring组件即通过反射使用有参构造方法创建bean:

    <bean id="person" class="com.company.demo2.Person">
     	<constructor-arg index="0" type="java.lang.String" value="张三">constructor-arg>
    	<constructor-arg index="1" type="java.lang.Integer" value="10">constructor-arg>
    bean>
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    MAC 地址简化概念(有线 MAC 地址、无线 MAC 地址、MAC 地址的随机化)
    走近高德驾车ETA(预估到达时间)
    Yarn学习,Yarn安装,Yarn常用命令。这一篇即可(有需要再补充)
    你的工具包已到货「GitHub 热点速览 v.22.31」
    20231106给cv180zb刷机的LOG
    01.Swagger配置
    物联网安全挑战
    oppo手机换荣耀手机,便签数据怎么搬家?
    大米自动化生产线的运作原理与科技创新
    Spark读取elasticsearch数据指南
  • 原文地址:https://blog.csdn.net/qyj19920704/article/details/126319058