• 详解 反射机制


    反射机制,可能有的同学在学习本篇博文的内容之前,就久仰过大名。
    因为,只要学习框架的知识,就必然会看到反射机制的应用。
    那么,为什么反射机制这么受欢迎呢?
    因为它功能十分强大。
    至于为什么本人在次对反射机制赞不绝口,请看博文内容:



    首先,本人先要来讲解下有关反射机制的一个很重要的知识点 —— 类的加载

    类的加载

    请观看本人博文 —— 《详解 类的加载》

    反射机制

    概述

    JAVA反射机制是在运行状态
    对于任意一个,都能够知道这个类的所有属性和方法
    对于任意一个对象,都能够调用它的任意一个方法和属性
    这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制
    要想解剖一个类,必须先要获取到该类的字节码文件对象
    而解剖使用的就是Class类中的方法
    所以先要获取到每一个字节码文件对应的Class类型的对象


    获取 class文件对象:

    获取 class文件对象三种方式

    • Object类getClass()方法
    • 任何一个对象静态属性class
    • Class类静态方法forName()
      public static Class forName(String className):
      className: 这个表示的是一个类对应的全类名(就是需要加上包名)

    那么,本人来展示下这三种方式:
    首先,本人给出一个含有两个成员、三种构造方法、三个方法的信息存储类:

    package edu.youzg.about_reflact.core;
    
    public class FanInfo {
        public String name;
        int age;
    
        public FanInfo() {
            System.out.println("执行类空参构造方法");
        }
    
        public FanInfo(String name) {
            this.name = name;
            System.out.println("执行类单参构造方法 === " + name);
        }
    
        private FanInfo(String name, int age) {
            this.name = name;
            this.age = age;
            System.out.println("执行类双参私有参构造方法 === " + name + " === " + age);
        }
    
        public void test(){
            System.out.println("这是一个空参的test方法");
        }
    
        public void test1 (String name){
            System.out.println("这是一个空参的test方法" + name);
        }
    
        public void test2(String name,int age){
            System.out.println("两个参数的方法"+name+"==="+age);
        }
    
    
        private int test(String name, int age, double num) {
            System.out.println("私有的方法" + name + "===" + age + "===" + num);
            return 666;
        }
    }
    

    那么,现在,本人来展示下如何获取 class文件对象:

    package edu.youzg.about_reflact.core;
    
    public class Test {
    
        public static void main(String[] args) throws ClassNotFoundException {
            //如何获取一个类的字节码文件对象。
            //方式1 getClasss()
            Object obj = new Object();
            Class aClass = obj.getClass();
            Class aClass1 = obj.getClass();
    
            Object obj2 = new Object();
            Class aClass2 = obj2.getClass();
    
            System.out.println(aClass==aClass1);
    
            System.out.println(aClass1==aClass2);
            System.out.println("=================================");
    
            //方式2,每一个类,都有一个静态的 class 属性可以获取该类的字节码文件对象
            Class objectClass = Object.class;
            System.out.println(aClass2==objectClass);
            Class stringClass = String.class;
    
            System.out.println("=================================");
    
            //方式3:使用Class中的方法 传入一个类的全限定名来获取
            //全限定名:包名+类名 能够确保一个类的唯一性。org.westos.demo.MyTest  org.westos.demo2.MyTest
            //Class 类 描述字节码文件类型的。
            //static Class<?> forName (String className)
            //返回与带有给定字符串名的类或接口相关联的 Class 对象。
    
            Class fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
            Class fanClass2 = Class.forName("edu.youzg.about_reflact.core.FanInfo");
            System.out.println(fanClass == fanClass2);
        }
    
    }
    

    现在,本人来展示下运行结果
    在这里插入图片描述


    获取 构造方法:

    获取所有构造方法

    • public Constructor<?>[] getConstructors():
      获取所有的构造方法,不包含私有的
    • public Constructor<?>[] getDeclaredConstructors():
      获取所有的构造方法 ,包括私有的

    获取单个构造方法

    • public Constructor< T > getConstructor(Class<?>... parameterTypes):
      获取单个的构造方法, 不包含私有的
    • public Constructor< T > getDeclaredConstructor(Class<?>... parameterTypes):
      获取单个的构造方法,包含私有的

    那么,本人来展示下如何 获取构造方法:

    package edu.youzg.about_reflact.core;
    
    import java.lang.reflect.Constructor;
    
    public class Test {
    
        public static void main(String[] args) throws Exception {
            //1.获取该类的字节码文件对象
            Class fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
            //2.获取该类的空参构造方法对象
            Constructor constructor = fanClass.getConstructor();
    
            //使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
            Object obj = constructor.newInstance();
            System.out.println(obj);
    
            System.out.println("========================================");
            //我们之前也可以借助有参构造来创建对象。
            //我们采用反射的方式,来借助有参构造,创建该类对象。
            //获取一个参数的构造方法对象。
            Constructor constructor1 = fanClass.getConstructor(String.class);
            //使用构造方法对象中的方法来创建该类对象。
            FanInfo obj2 = (FanInfo) constructor1.newInstance("米斯达");
            System.out.println(obj2);
    
            System.out.println("========================================");
            //我们以前不能使用私有构造直接创建对象
            //下来我们采用反射的方式的方式,借助私有构造方法来创建出该类对象
            //获取出私有构造方法对象
            Constructor declaredConstructor = fanClass.getDeclaredConstructor(String.class, int.class);
            //通过私有的构造方法对象,调用他的方法来创建该类对象。
            //取消权限的语法检测
            declaredConstructor.setAccessible(true);
            Object obj7 = declaredConstructor.newInstance("波鲁纳雷夫", 30);
            System.out.println(obj7);
        }
    
    }
    

    现在,本人来展示下运行结果
    在这里插入图片描述


    获取 类的对象:

    获取 类的对象

    • public Object newInstance():
      根据调用这个方法的Constructor类对象,获得一个相应的对象

    那么,本人来展示下如何 获取类的对象:

    package edu.youzg.about_reflact.core;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    public class Test {
    
        public static void main(String[] args) throws Exception {
            //我们采用反射的方式,来创建一个类的对象。
            //1.获取该类的字节码文件对象
            Class fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
            //2.获取空参的构造方法对象
            Constructor declaredConstructor = fanClass.getDeclaredConstructor();
            //3.调用 Constructor 中的方法来创建FanInfo的对象
            FanInfo fan = (FanInfo) declaredConstructor.newInstance();
            System.out.println(fan);
    
            System.out.println("===================================");
            //我们现在来通过双参构造方法来创建FanInfo的对象
            Constructor constructor = fanClass.getDeclaredConstructor(String.class, int.class);
            constructor.setAccessible(true);
            //设置权限
            fan = (FanInfo) constructor.newInstance("米斯达", 30);
            System.out.println(fan);
    
            System.out.println("===================================");
            //如果我们借助 空参 来创建对象,还有一个简便方法。
    
            Class fanClass2 = Class.forName("edu.youzg.about_reflact.core.FanInfo");
            //不用去获取空参构造对象,在 Class 里面有一个方法,就能创建对象。
            FanInfo fan2 = (FanInfo) fanClass2.newInstance();
            System.out.println(fan2);
        }
    
    }
    

    那么,本人现在来展示下运行结果
    在这里插入图片描述


    获取成员变量:

    获取所有成员变量

    • public Field[] getFields():
      获取所有的成员变量包含从父类继承过来的
    • public Field[] getDeclaredFields():
      获取所有的成员变量 包含私有的 也包含从父类继承过来的成员变量

    获取单个成员变量

    • public Field getField(String name)
    • public Field getDeclaredField(String name)

    那么,本人来展示下如何 获取成员变量:

    package edu.youzg.about_reflact.core;
    
    import java.lang.reflect.Field;
    
    public class Test {
    
        public static void main(String[] args) throws Exception {
            //对于成员变量 用 Field 这个类型来描述
            //1.获取该类的字节码文件对象
            Class fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
            //2.获取该类的成员变量对象
            //2.1 获取该类中所有的字段对象数组
            //getFields() 获取所有的公共字段
            Field[] fields = fanClass.getFields();
            for (Field field : fields) {
                System.out.println(field.toString());
            }
    
            System.out.println("==================================");
            //获取所有字段对象,包括私有
            Field[] declaredFields = fanClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                System.out.println(declaredField);
            }
    
            System.out.println("===========================================================");
            //获取公共的单个字段对象
            Field name = fanClass.getField("name");//传入字段名称
            System.out.println(name);
            //获取非公共的单个字段对象。
            Field age = fanClass.getDeclaredField("age");
            System.out.println(age);
        }
    
    }
    

    那么,本人现在来展示下运行结果:
    在这里插入图片描述


    获取成员方法:

    获取所有成员方法

    • public Method[] getMethods():
      获取所有的公共的成员方法不包含私有的
      包含从父类继承过来的过来的公共方法
    • public Method[] getDeclaredMethods():
      获取自己的所有成员方法 包含私有的

    获取单个成员方法

    //参数1: 方法名称 参数2:方法行参的class 对象

    • public Method getMethod(String name,Class<?>... parameterTypes):
      获取单个的方法 不包含私有的
    • public Method getDeclaredMethod(String name,Class<?>... parameterTypes):
      获取单个方法包括私有的

    那么,本人现在来展示下 获取成员方法:

    package edu.youzg.about_reflact.core;
    
    import java.lang.reflect.Method;
    
    public class Test {
    
        public static void main(String[] args) throws Exception {
            //我们采用反射的方式来调用方法执行。
    
            //1.获取该类的字节码文件对象
            Class<?> fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
            //2.获取空参的方法对象
            Method testMethod = fanClass.getMethod("test");
            //3.调用Method类中的 invoke() 方法,让test 调用执行
            Object obj = fanClass.newInstance();
            testMethod.invoke(obj); //参数1,类的对象,参数2,给方法的形参传递的实际的值
    
            System.out.println("================================");
            //调用两个参数的方法执行
            Method test1 = fanClass.getMethod("test1", String.class);
            //参数1,类的对象,参数2,给方法的形参传递的实际的值
            test1.invoke(obj, "米斯达");
    
            System.out.println("==================================");
            Method test2 = fanClass.getMethod("test2", String.class, int.class);
            test2.invoke(obj, "米斯达", 30);
    
            System.out.println("==================================");
            //以前的方式,你在外界new对象调用 私有方法是调用不到的。
            //我们通过反射来调用私有方法执行。
            Method test3 = fanClass.getDeclaredMethod("test3", String.class, int.class, double.class);
            //调用私有方法,取消私有的权限校验
            test3.setAccessible(true);
            Integer result = (Integer) test3.invoke(obj, "波鲁纳雷夫", 30, 66.6);
            System.out.println(result);
        }
    
    }
    

    那么,本人现在来展示下运行结果
    在这里插入图片描述


    现在,本人要讲解一个非常常用的反射机制的应用场景 —— 通过反射运行配置文件内容

    反射运行配置文件内容:

    在本人《详解 Properties类》博文中,曾讲过:Properties类可以将配置文件中的键值对读取出来。

    那么,在这里,本人将通过反射机制,将从配置文件中读取出来的值,赋给类中相应成员:

    本人直接来上代码:
    首先,本人来给出一个多功能手机类:

    package edu.youzg.about_reflact.core;
    
    public class Phone {
    
        public Phone() {
        }
        
        public void game() {
            System.out.println("正在运行游戏");
        }
        
        public void music() {
            System.out.println("正在播放音乐");
        }
    
        public void call() {
            System.out.println("正在进行通话");
        }
        
    }
    

    现在,本人来展示下配置文件的信息:

    className=edu.youzg.about_reflact.core.Phone
    methodName=music
    

    那么,现在,本人来展示下通过反射机制扫描配置文件,来调用目标方法:

    package edu.youzg.about_reflact.core;
    
    import java.io.FileReader;
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    public class Test {
    
        public static void main(String[] args) throws Exception {
            //加载配置文件
            //基于目标类开发
            Properties properties = new Properties();
            properties.load(new FileReader("./config.properties"));
            //1.获取该类的字节码文件对象
            Class clazz = Class.forName(properties.getProperty("className"));
            //2.通过反射来创建目标类对象
            Object obj = clazz.getDeclaredConstructor().newInstance();
            //3.调用目标类中的方法执行
            Method methodName = clazz.getMethod(properties.getProperty("methodName"));
            methodName.invoke(obj);
        }
    
    }
    

    那么,本人来展示下运行结果
    在这里插入图片描述


    反射忽视泛型检查:

    在本人博文《详解 泛型 与 自动拆装箱》中讲到过:

    泛型仅在编译期有效,而反射机制是针对运行期的。
    所以,我们若是通过反射机制去调用泛型的话,就会读取不到相应的类型,只会读取到Object类型,这就是我们所谓的 泛型擦除机制

    那么,现在,本人就来展示下通过反射机制去调用一个集合时,可以存入不恰当的数据:

    package edu.youzg.about_reflact.core;
    
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    
    public class Test {
    
        public static void main(String[] args) throws Exception {
            ArrayList<Integer> list = new ArrayList<>();
            list.add(666);
            list.add(374);
            list.add(251);
            // list.add("abc");
            //泛型,只在编译期有效,运行期就擦除了。
            //ArrayList.class
            Class aClass = list.getClass();
            //获取add()方法对象
            Method add = aClass.getMethod("add", Object.class);
            add.invoke(list,"abc");
    
            System.out.println(list);
        }
    
    }
    

    那么,本人来展示下运行结果
    在这里插入图片描述
    可以看到,Integer类型的ArrayList,存入了字符串数据!


    接下来,本人来通过反射机制给出一个工具类,以便我们能够直接对一个对象的某成员赋值:

    反射机制设置某个对象的某个属性为指定的值:

    首先,是这个工具类

    package edu.youzg.about_reflact.core;
    
    import java.lang.reflect.Field;
    
    public class AssignUtil {
    
        /* 此方法可将obj对象中名为propertyName的属性的值设置为value*/
        public static void setProperty(Object obj, String propertyName, Object value) throws NoSuchFieldException, IllegalAccessException {
            Class aClass = obj.getClass();
            Field declaredField = aClass.getDeclaredField(propertyName);
            declaredField.setAccessible(true); //取消private权限的检测
            declaredField.set(obj,value);
        }
    }
    

    现在,本人来给出一个测试类:

    package edu.youzg.about_reflact.core;
    
    public class Test {
    
        public static void main(String[] args) throws Exception {
            //通过反射写一个通用的设置某个对象的某个属性为指定的值
            FanInfo FanInfo = new FanInfo();
            AssignUtil.setProperty(FanInfo,"name","波鲁纳雷夫");
            AssignUtil.setProperty(FanInfo,"age",30);
            System.out.println(FanInfo.name);
            System.out.println(FanInfo.age);
        }
    
    }
    

    那么,现在,本人来展示下运行结果
    在这里插入图片描述


    那么,现在,本人再来讲解一个有关反射机制的很重要的知识点 —— 动态代理

    动态代理

    请观看本人博文—— 《详解 动态代理》


  • 相关阅读:
    二叉树
    队列和栈
    时间复杂度和空间复杂度
    二分查找法
    排序算法值归并排序
    排序算法之选择排序类
    5.7.1.3 Global 对象的属性
    5.7.1.2 eval() 方法
    5.7.1.1 单体内置对象
    5.6.3.8 fromCharCode()方法
  • 原文地址:https://www.cnblogs.com/codderYouzg/p/12419061.html
Copyright © 2020-2023  润新知