• Java基础篇反射机制总结



    说明

         康师傅说:"获取Class类的实例、获取运行时类对象、调用运行时类的指定结构这三个是比较重要的,需要掌握,其他的理解为主“。

    1. 反射基础知识预备

    1.1 反射机制概述

    概念描述

         ① Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
        ②加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

    Java反射机制提供的功能

    ①在运行时判断任意一个对象所属的
    ②在运行时构造任意一个类的对象
    ③在运行时判断任意一个类所具有的成员变量方法
    ④在运行时获取泛型信息
    ⑤在运行时调用任意一个对象的成员变量方法
    ⑥在运行时处理注解
    ⑦生成动态代理

    反射相关的主要API

    ①java.lang.Class:代表一个
    ②java.lang.reflect.Method:代表类的方法
    ③java.lang.reflect.Field:代表类的成员变量
    ④java.lang.reflect.Constructor:代表类的构造器

    1.2 Class类的理解

    Class类的概述

        Object类中定义了以下的方法,此方法将被所有子类继承:public final Class getClass()
        以上的方法返回值的类型是一个Class类,此类是Java反射源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称
        通过Class类对象的调用可以可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些口。

    获取Class类的实例

    四种实现方法(第四类不做说明,基本不用):

    ①调用运行时类的属性.class
    ②通过运行时类的对象,调用getClass()
    ③调用Class的静态方法:forName(String classPath)classPath为全路径

    实现代码展示

    	//获取运行时类
        @Test
        public void test3() throws ClassNotFoundException {
            //方式一:调用运行时类的属性.class
            Class<Person> clazz1 = Person.class;
            System.out.println(clazz1);
    
            //方式二:通过运行时类的对象,调用getClass()
            Person p1 = new Person();
            Class<? extends Person> clazz2 = p1.getClass();
            System.out.println(clazz2);
    
            //方式三:调用Class的静态方法:forName(String classPath)classPath为全路径
            Class<?> clazz3 = Class.forName("day18.com.it.java0.Person");
            System.out.println(clazz3);
    
            System.out.println(clazz1 == clazz2);
            System.out.println(clazz1 == clazz3);
    
            ClassLoader classLoader = ReflectionTest.class.getClassLoader();
            Class<?> clazz4 = classLoader.loadClass("day18.com.it.java0.Person");
            System.out.println(clazz4);
    
            System.out.println(clazz1 == clazz4);
        }
    
    • 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

    实现结果

    在这里插入图片描述

    1.3 类的加载与ClassLoader的理解

    类的加载过程

         当程序主动使用某个类时,如果该类还未被加载内存中,则系统会通过如下三个步骤来对该类进行初始化

    在这里插入图片描述
    ClassLoader的理解

    类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的类的加载器。

    三种加载器
        ①引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取扩展
        ②类加载器:负责jre/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库
        ③系统类加载器:负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工作 ,是最常用的加载
    利用Load实现配置文件的获取

    public class LoadTest {
        /*
        测试:使用Properties来读取配置文件
         */
        @Test
        public void test1() throws Exception {
            Properties pros = new Properties();
            //此时的默认文件在当前的module下
            //读取配置文件的方式一:
            FileInputStream fis = new FileInputStream("jdbc.properties");
            pros.load(fis);
    
            //此时的默认文件在当前module的src下
            //读取配置文件的方式二:
    //        ClassLoader clazz = LoadTest.class.getClassLoader();
    //        InputStream is = clazz.getResourceAsStream("jdbc1.properties");
    //        pros.load(is);
            String name = pros.getProperty("name");
            String password = pros.getProperty("password");
            System.out.println("name = " + name + ", password = " + password);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    2. 反射机制的具体实现

    2.1 获取运行时类对象

    ①根据全类名获取对应的Class对象
    ②调用指定参数结构的构造器,生成Constructor的实例
    ③通过Constructor的实例创建对应类的对象,并初始化类属性
    通过newInstance()获取运行时类的对象

    @Test
        public void test1() throws InstantiationException, IllegalAccessException {
    
            Class<Person> clazz = Person.class;
            /*
            new Instance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参构造器
    
            要想此方法正常的创建运行时类的对象,要求:
            1.运行时类必须提供空参构造器
            2.空参构造器的访问权限得够。通常,设置为public.
    
            在javabean中提供一个public的空参构造器。原因:
            1.便于通过反射,创建运行时类的对象
            2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
             */
            Person person = clazz.newInstance();
            System.out.println(person);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    动态获取运行时类的对象

    	@Test
        public void test2(){
            int num = new Random().nextInt(3);
            String classPath = "";
            switch (num){
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.sql.Date";
                    break;
                case 2:
                    classPath = "day18.com.it.java0.Person";
                    break;
            }
            try {
                Object obj = getInstance(classPath);
                System.out.println(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
    
        public Object getInstance(String classPath) throws Exception {
            Class clazz = Class.forName(classPath);
            return clazz.newInstance();
        }
    
    • 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

    在这里插入图片描述

    2.2 获取运行时类的完整结构

         通过反射获取运行时类的完整结构:Field(属性)、Method(方法)、Constructor(构造器)、Superclass(父类)、Interface(接口)、Annotation(注解)

    理解即可,不用掌握

    对于这些直接看代码理解更好一点
    获取属性

    @Test
        public void test1(){
    
            Class<Person> clazz = Person.class;
            //获取属性结构
            //getFields():获取当前运行类及其父类中声明为public访问权限的属性
            Field[] fields = clazz.getFields();
            for (Field f : fields){
                System.out.println(f);
            }
    
            System.out.println("********************");
    
            //getDeclaredFields()获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field f : declaredFields){
                System.out.println(f);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    通过反射访问属性的权限修饰符 数据类型 变量名

     //通过反射访问权限修饰符  数据类型  变量名
        @Test
        public void test2(){
    
            Class<Person> clazz = Person.class;
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field f : declaredFields){
    
                //权限修饰符
                int modifier = f.getModifiers();
                System.out.print(Modifier.toString(modifier) + "\t");
    
                //数据类型
                Class<?> type = f.getType();
                System.out.print(type + "\t");
    
                //变量名
                String name = f.getName();
                System.out.println(name);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    获取方法

     @Test
        public void test1() {
            //getMethods()获取当前运行时类及其父类中声明为public权限的方法
            Class<Person> clazz = Person.class;
            Method[] methods = clazz.getMethods();
            for (Method m : methods) {
                System.out.println(m);
            }
    
            //getDeclaredMethods()获取当前运行时类中声明的所有方法
            //获取当前运行时类中所声明的所有方法
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method m : declaredMethods) {
                System.out.println(m);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    获取方法的权限修饰符 返回值类型 方法名(形参列表)throws 异常,以及注解

    @Test
        public void test2() {
            Class<Person> clazz = Person.class;
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method m : declaredMethods) {
                //获取注解
                Annotation[] annotations = m.getAnnotations();
                for (Annotation a : annotations) {
                    System.out.println(a);
                }
    
                //获取权限修饰符
                System.out.print(Modifier.toString(m.getModifiers()) + "\t");
    
                //返回值类型
                Class<?> returnType = m.getReturnType();
                System.out.print(returnType + "\t");
    
                //方法名
                System.out.print(m.getName());
    
                //形参列表
                System.out.print("(");
                Class<?>[] parameterTypes = m.getParameterTypes();//获取当前方法的形参列表结合
                if (!(parameterTypes == null && parameterTypes.length == 0)) {
                    for (int i = 0; i < parameterTypes.length; i++) {
                        if (i == parameterTypes.length - 1) {
                            System.out.print(parameterTypes[i].getName() + "args_" + i);
                        }
                        System.out.print(parameterTypes[i].getName() + "args_" + i + ",");
                    }
                    System.out.print(")");
                }
    
                //获取异常信息
    
                Class<?>[] exceptionTypes = m.getExceptionTypes();
                if (exceptionTypes.length > 0) {
                    System.out.print("throws");
                    for (int i = 0; i < exceptionTypes.length; i++) {
                        if (i == exceptionTypes.length - 1) {
                            System.out.print(exceptionTypes[i].getName());
                        }
                        System.out.print(exceptionTypes[i].getName() + ",");
                    }
                }
    
                System.out.println();
            }
        }
    
    • 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
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    在这里插入图片描述

    获取构造器结构

    //获取构造器结构
        @Test
        public void test1() throws NoSuchMethodException {
            //getConstructors():获取当前运行时类中声明为public的构造器
            Class<Person> clazz = Person.class;
            Constructor<?>[] constructor = clazz.getConstructors();
            for (Constructor c : constructor){
                System.out.println(c);
            }
    
            System.out.println();
            //getDeclaredConstructors():获取当前运行时类中声明的所有构造器
            Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
            for (Constructor c : declaredConstructors){
                System.out.println(c);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述
    对于其他的使用相同的方法即可获取,这里就不展示了

    2.3 调用运行时类的指定结构

    获取指定的属性、方法、构造器
    这里比较重要需要掌握
    调用运行时类中指定的属性

    @Test
        public void testField1() throws Exception{
            //获取Class类的实例
            Class<Person> clazz = Person.class;
            //2.获取一个运行时类的对象
            Person p1 = clazz.newInstance();
            //3.getDeclaredField():获取指定的属性
            Field name = clazz.getDeclaredField("name");
            //4.将属性设置为可用
            name.setAccessible(true);
            //5.设置属性数值
            name.set(p1,"Jack");
            System.out.println(name.get(p1));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    获取指定的方法

    @Test
        public void testMethod() throws Exception {
            //创建Class类的实例
            Class<Person> clazz = Person.class;
            //创建运行时类的对象
            Person p1 = clazz.newInstance();
            //获取指定的某个方法
            //getDeclaredMethod():参数1:知名获取的方法名称 参数2:指明获取方法的参数列表
            Method show = clazz.getDeclaredMethod("show", String.class);
            //保证此方法是可访问的
            show.setAccessible(true);
            //调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
            //invoke()的返回值即为对应类中调用方法的返回值
            Object obj = show.invoke(p1, "CHN");
            System.out.println(obj);
    
            System.out.println("*************如何调用静态方法***************");
            //没有返回值是调用方法返回的是null
            Method showDesc = clazz.getDeclaredMethod("showDesc");
            showDesc.setAccessible(true);
            Object o = showDesc.invoke(p1);
            System.out.println(o);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    获取指定的构造器

    	@Test
        public void testConstructor() throws Exception{
            //创建Class类的实例
            Class<Person> clazz = Person.class;
            //获取指定的构造器
            Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class);
            //保证获取的构造器是可用的
            constructor.setAccessible(true);
            //创建一个运行时类的对象
            Person person = constructor.newInstance("Tom");
            System.out.println(person);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.4 动态代理

    代理设计模式的原理

         ①使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
         ②静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
         ③动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
         ④动态代理使用场合:
                   (1)调试
                   (2)远程方法调用

    动态代理步骤

    ①创建一个实现接口InvocationHandler的类,它必须实现invoke方法,以完成代理的具体操作。
    ②创建被代理的以及接口
    ③通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个Subject接口代理
    ④通过 Subject代理调用RealSubject实现类的方法

  • 相关阅读:
    javaee springMVC日期类型转换之通过注解的方式
    机器学习库Scikit-Learn
    vue3——pixi初学,编写一个简单的小游戏,复制粘贴可用学习
    flink-sql所有表连接器
    基于springboot教学管理系统设计与实现
    内核内存管理
    kubernetesr进阶--条件化的污点(TaintNodesByCondition)
    大型医院容灾备份平台建设与应用实践
    中集集团全球港航AI高科技独角兽中集飞瞳港口航运新枢纽人工智能技术走向成熟全球前三大船公司及港口码头应用落地港口智能化码头智能化
    基于PyQt5GUI的人脸识别系统设计与实现
  • 原文地址:https://blog.csdn.net/weixin_44606952/article/details/124977597