展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > java反射机制及常用应用场景是什么

java反射机制及常用应用场景是什么

一、什么是java反射?在java的面向对象编程过程中,通常我们需要先知道一个Class类,然后new类名()方式来获取该类的对象。也就是说我们需要在写代码的时候(编译期或者类加载之前)就知道我们要实例化哪一个类,运行哪一个方法,这种通常被称为静态的类加载。但是在有些场景下,我们事先是不知道我们的代码的具体行为的。比如,我们定义一个服务任务工作流,每一个服务任务都是对应的一个类的一个方法。服务任务B执行哪一个类的哪一个方法,是由服务任务A的执行结果决定的服务任务C执行哪一个类的哪一个方法,是由服务任务A和

本文目录

      一、什么是java反射?

      在java的面向对象编程过程中,通常我们需要先知道一个Class类,然后new 类名()方式来获取该类的对象。也就是说我们需要在写代码的时候(编译期或者类加载之前)就知道我们要实例化哪一个类,运行哪一个方法,这种通常被称为静态的类加载。

      但是在有些场景下,我们事先是不知道我们的代码的具体行为的。比如,我们定义一个服务任务工作流,每一个服务任务都是对应的一个类的一个方法。

      java反射机制及常用应用场景是什么

      • 服务任务B执行哪一个类的哪一个方法,是由服务任务A的执行结果决定的

      • 服务任务C执行哪一个类的哪一个方法,是由服务任务A和B的执行结果决定的

      • 并且用户不希望服务任务的功能在代码中写死,希望通过配置的方式根据不同的条件执行不同的程序,条件本身也是变化的

      面对这个情况,我们就不能用代码new 类名()来实现了,因为你不知道用户具体要怎么做配置,这一秒他希望服务任务A执行Xxxx类的x方法,下一秒他可能希望执行Yyyy类的y方法。当然你也可以说提需求嘛,用户改一次需求,我改一次代码。这种方式也能需求,但对于用户和程序员个人而言都是痛苦,那么有没有一种方法在运行期动态的改变程序的调用行为的方法呢?这就是要为大家介绍的“java反射机制”。

      那么java的反射机制能够做那些事呢?大概是这样几种:

      • 在程序运行期动态的根据package名.类名实例化类对象

      • 在程序运行期动态获取类对象的信息,包括对象的成本变量和方法

      • 在程序运行期动态使用对象的成员变量属性

      • 在程序运行期动态调用对象的方法(私有方法也可以调用)

      二、Hello World

      先入个门,大佬可以略过这一段。我们定义一个类叫做Student

      package com.zimug.java.reflection;
      public class Student {
          public String nickName;
          private Integer age;   //这里是private
          public void dinner(){
              System.out.println("吃晚餐!");
          }
          private void sleep(int minutes){   //private修饰符
              System.out.println("睡" + minutes + "分钟");
          }
      }

      如果不用反射的方式,我相信只要学过java的朋友肯定会调用dinner方法

      Student student = new Student();
      student.dinner();

      如果是反射的方式我们该怎么调用呢?

      //获取Student类信息
      Class cls = Class.forName("com.zimug.java.reflection.Student");
      //对象实例化
      Object obj = cls.getDeclaredConstructor().newInstance();
      //根据方法名获取并执行方法
      Method dinnerMethod = cls.getDeclaredMethod("dinner");
      dinnerMethod.invoke(obj);  //打印:吃晚餐!

      通过上面的代码我们看到,com.zimug.java.reflection.Student类名和dinner方法名是字符串。既然是字符串我们就可以通过配置文件,或数据库、或什么其他的灵活配置方法来执行这段程序了。这就是反射最基础的使用方式。

      三、类加载与反射关系

      java的类加载机制还是挺复杂的,我们这里为了不混淆重点,只为大家介绍和“反射”有关系的一部分内容。

      java执行编译的时候将java文件编译成字节码class文件,类加载器在类加载阶段将class文件加载到内存,并实例化一个java.lang.Class的对象。比如对于Student类在加载阶段会有如下动作:

      • 在内存(方法区或叫代码区)中实例化一个Class对象,注意是Class对象不是Student对象

      • 一个Class类(字节码文件)对应一个Class对象,并且只有一个

      • 该Class对象保存了Student类的基础信息,比如这个Student类有几个字段(Filed)?有几个构造方法(Constructor)?有几个方法(Method)?有哪些注解(Annotation)?等信息

      java反射机制及常用应用场景是什么

      有了上面的关于Student类的基本信息对象(java.lang.Class对象),在运行期就可以根据这些信息来实例化Student类的对象。

      • 在运行期你可以直接new一个Student对象

      • 也可以使用反射的方法构造一个Student对象

      java反射机制及常用应用场景是什么

      但是无论你new多少个Student对象,不论你反射构建多少个Student对象,保存Student类信息的java.lang.Class对象都只有一个。下面的代码可以证明。

      Class cls = Class.forName("com.zimug.java.reflection.Student");
      Class cls2 = new Student().getClass();
      System.out.println(cls == cls2); //比较Class对象的地址,输出结果是true

      四、操作反射的java类

      了解了上面的这些基础信息,我们就可以更深入学习反射类相关的类和方法了:

      java.lang.Class: 代表一个类

      java.lang.reflect.Constructor: 代表类的构造方法

      java.lang.reflect.Method: 代表类的普通方法

      java.lang.reflect.Field: 代表类的成员变量

      Java.lang.reflect.Modifier: 修饰符,方法的修饰符,成员变量的修饰符。

      java.lang.annotation.Annotation:在类、成员变量、构造方法、普通方法上都可以加注解

      4.1.获取Class对象的三种方法

      Class.forName()方法获取Class对象

      /**
      * Class.forName方法获取Class对象,这也是反射中最常用的获取对象的方法,因为字符串传参增强了配置实现的灵活性
      */
      Class cls = Class.forName("com.zimug.java.reflection.Student");

      类名.class获取Class对象

      /**
      * `类名.class`的方式获取Class对象
      */
      Class clz = User.class;

      类对象.getClass()方式获取Class对象

      /**
      * `类对象.getClass()`方式获取Class对象
      */
      User user = new User();
      Class clazz = user.getClass();

      虽然有三种方法可以获取某个类的Class对象,但是只有第一种可以被称为“反射”。

      4.2.获取Class类对象的基本信息

      Class cls = Class.forName("com.zimug.java.reflection.Student");
      //获取类的包名+类名
      System.out.println(cls.getName());          //com.zimug.java.reflection.Student
      //获取类的父类
      Class cls = Class.forName("com.zimug.java.reflection.Student");
      //这个类型是不是一个注解?
      System.out.println(cls.isAnnotation());     //false
      //这个类型是不是一个枚举?
      System.out.println(cls.isEnum());      //false
      //这个类型是不是基础数据类型?
      System.out.println(cls.isPrimitive()); //false

      Class类对象信息中几乎包括了所有的你想知道的关于这个类型定义的信息,更多的方法就不一一列举了。还可以通过下面的方法

      获取Class类对象代表的类实现了哪些接口: getInterfaces()

      获取Class类对象代表的类使用了哪些注解: getAnnotations()

      4.3. 获得Class对象的成员变量

      结合上文中的Student类的定义理解下面的代码

      Class cls = Class.forName("com.zimug.java.reflection.Student");
      Field[] fields = cls.getFields();
      for (Field field : fields) {
      System.out.println(field.getName());      //nickName
      }
      fields = cls.getDeclaredFields();
      for (Field field : fields) {
      System.out.println(field.getName());      //nickName 换行  age
      }

      getFields()方法获取类的非私有的成员变量,数组,包含从父类继承的成员变量

      getDeclaredFields方法获取所有的成员变量,数组,但是不包含从父类继承而来的成员变量

      4.4.获取Class对象的方法

      getMethods() : 获取Class对象代表的类的所有的非私有方法,数组,包含从父类继承而来的方法

      getDeclaredMethods() : 获取Class对象代表的类定义的所有的方法,数组,但是不包含从父类继承而来的方法

      getMethod(methodName): 获取Class对象代表的类的指定方法名的非私有方法

      getDeclaredMethod(methodName): 获取Class对象代表的类的指定方法名的方法

              Class cls = Class.forName("com.zimug.java.reflection.Student");
              Method[] methods = cls.getMethods();
              System.out.println("Student对象的非私有方法");
              for (Method m : methods) {
                  System.out.print(m.getName() + ",");
              }
              System.out.println("  end");
              Method[] allMethods = cls.getDeclaredMethods();
              System.out.println("Student对象的所有方法");
              for (Method m : allMethods) {
                  System.out.print(m.getName() + ",");
              }
              System.out.println("  end");
              Method dinnerMethod = cls.getMethod("dinner");
              System.out.println("dinner方法的参数个数" + dinnerMethod.getParameterCount());
              Method sleepMethod = cls.getDeclaredMethod("sleep",int.class);
              System.out.println("sleep方法的参数个数" + sleepMethod.getParameterCount());
              System.out.println("sleep方法的参数对象数组" + Arrays.toString(sleepMethod.getParameters()));
              System.out.println("sleep方法的参数返回值类型" + sleepMethod.getReturnType());

      上面代码的执行结果如下:

      Student对象的非私有方法
      dinner,wait,wait,wait,equals,toString,hashCode,getClass,notify,notifyAll,  end
      Student对象的所有方法
      dinner,sleep,  end
      dinner方法的参数个数0
      sleep方法的参数个数1
      sleep方法的参数对象数组[int arg0]
      sleep方法的参数返回值类型void

      可以看到getMethods获取的方法中包含Object父类中定义的方法,但是不包含本类中定义的私有方法sleep。另外我们还可以获取方法的参数及返回值信息:

      获取参数相关的属性:

      • 获取方法参数个数:getParameterCount()

      • 获取方法参数数组对象:getParameters() ,返回值是java.lang.reflect.Parameter数组

      获取返回值相关的属性

      • 获取方法返回值的数据类型:getReturnType()

      4.5.方法的调用

      实际在上文中已经演示了方法的调用,如下invoke调用dinner方法

      Method dinnerMethod = cls.getDeclaredMethod("dinner");
      dinnerMethod.invoke(obj);  //打印:吃晚餐!

      dinner方法是无参的,那么有参数的方法怎么调用?看看invoke方法定义,第一个参数是Method对象,无论后面Object... args有多少参数就按照方法定义依次传参就可以了。

      public Object invoke(Object obj, Object... args)

      4.6.创建类的对象(实例化对象)

      //获取Student类信息
      Class cls = Class.forName("com.zimug.java.reflection.Student");
      //对象实例化
      Student student = (Student)cls.getDeclaredConstructor().newInstance();
      //下面的这种方法是已经Deprecated了,不建议使用。但是在比较旧的JDK版本中仍然是唯一的方式。
      //Student student = (Student)cls.newInstance();

      五、反射的常用场景

      通过配置信息调用类的方法

      结合注解实现特殊功能

      按需加载jar包或class

      5.1. 通过配置信息调用类的方法

      将上文的hello world中的代码封装一下,你知道类名className和方法名methodName是不是就可以调用方法了?至于你将className和 methodName配置到文件,还是nacos,还是数据库,自己决定吧!

      public void invokeClassMethod(String className,String methodName) throws ClassNotFoundException, 
                  NoSuchMethodException, 
                  InvocationTargetException, 
                  InstantiationException, 
                  IllegalAccessException {
              //获取类信息
              Class cls = Class.forName(className);
              //对象实例化
              Object obj = cls.getDeclaredConstructor().newInstance();
              //根据方法名获取并执行方法
              Method dinnerMethod = cls.getDeclaredMethod(methodName);
              dinnerMethod.invoke(obj);
      }

      5.2.结合注解实现特殊功能

      大家如果学习过mybatis plus都应该学习过这样的一个注解TableName,这个注解表示当前的实体类Student对应的数据库中的哪一张表。如下问代码所示,Student所示该类对应的是t_student这张表。

      @TableName("t_student")
      public class Student {
          public String nickName;
          private Integer age;
      }

      下面我们自定义TableName这个注解

      @Target(ElementType.TYPE)  //表示TableName可作用于类、接口或enum Class, 或interface
      @Retention(RetentionPolicy.RUNTIME) //表示运行时由JVM加载
      public @interface TableName {
             String value() ;   //则使用@TableName注解的时候: @TableName(”t_student”);
      }

      有了这个注解,我们就可以扫描某个路径下的java文件,至于类注解的扫描我们就不用自己开发了,引入下面的maven坐标就可以

      
          org.reflections
          reflections
          0.9.10
      

      看下面代码:先扫描包,从包中获取标注了TableName注解的类,再对该类打印注解value信息

      // 要扫描的包
      String packageName = "com.zimug.java.reflection";
      Reflections f = new Reflections(packageName);
      // 获取扫描到的标记注解的集合
      Set> set = f.getTypesAnnotatedWith(TableName.class);
      for (Class c : set) {
      // 循环获取标记的注解
      TableName annotation = c.getAnnotation(TableName.class);
      // 打印注解中的内容
      System.out.println(c.getName() + "类,TableName注解value=" + annotation.value());

      输出结果是:

      com.zimug.java.reflection.Student类,TableName注解value=t_student

      有的朋友会问这有什么用?这有大用处了。有了类定义与数据库表的对应关系,你还能通过反射获取类的成员变量,之后你是不是就可以根据表明t_student和字段名nickName,age构建增删改查的SQL了?全都构建完毕,是不是就是一个基础得Mybatis plus了?

      反射和注解结合使用,可以演化出许许多多的应用场景,特别是在框架代码实现方面。等待你去发觉啊!

      5.3.按需加载jar包或class

      在某些场景下,我们可能不希望JVM的加载器一次性的把所有classpath下的jar包装载到JVM虚拟机中,因为这样会影响项目的启动和初始化效率,并且占用较多的内存。我们希望按需加载,需要用到哪些jar,按照程序动态运行的需求取加载这些jar。

      把jar包放在classpath外面,指定加载路径,实现动态加载。

      //按路径加载jar包
      File file = new File("D:/com/zimug/commons-lang3.jar");
      URL url = file.toURI().toURL();
      //创建类加载器
      ClassLoader classLoader = new URLClassLoader(new URL[]{url});
      Class cls = classLoader.loadClass("org.apache.commons.lang3.StringUtils");

      同样的把.class文件放在一个路径下,我们也是可以动态加载到的

      //java的.class文件所在路径
      File file = new File("D:/com/zimug");
      URL url = file.toURI().toURL();
      //创建类加载器
      ClassLoader classLoader = new URLClassLoader(new URL[]{url});
      //加载指定类,package全路径
      Class cls = classLoader.loadClass("com.zimug.java.reflection.Student");

      类的动态加载能不能让你想到些什么?是不是可以实现代码修改,不需要重新启动web容器?对的,就是这个原理,因为一个类的Class对象只有一个,所以不管你重新加载多少次,都是使用最后一次加载的class对象(上文讲过哦)。

      六、反射的优缺点

      优点:自由,使用灵活,不受类的访问权限限制。可以根据指定类名、方法名来实现方法调用,非常适合实现业务的灵活配置。在框架开发方面也有非常广泛的应用,特别是结合注解的使用。

      缺点:

      • 也正因为反射不受类的访问权限限制,其安全性低,很大部分的java安全问题都是反射导致的。

      • 相对于正常的对象的访问调用,反射因为存在类和方法的实例化过程,性能也相对较低

      • 破坏java类封装性,类的信息隐藏性和边界被破坏

      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发 Java
      相关文章 更多
      精品专题 更多
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      macOS软件

      正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

      IOS软件

      正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

      AI

      正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

      Mac软件 更多
      Postman macOS版
      Postman macOS版

      Postman是一套面向API全生命周期的开发与测试平台,可在Mac上发送和调试多种协议请求,管理集合、环境及认证信息,并完成脚本测试、模拟服务、监控、文档发布和团队协作。它适合开发、测试及运维人员使用,提供个人免费方案。

      Docker Desktop macOS版
      Docker Desktop macOS版

      Docker Desktop 是 Docker 官方为 Mac 提供的容器开发环境,集成 Docker Engine、命令行工具、Compose、Build、Kubernetes 与图形化管理界面。

      Android Studio macOS版
      Android Studio macOS版

      Android Studio 是 Google 面向安卓应用开发提供的官方集成开发环境,基于 IntelliJ IDEA 构建。它整合智能代码编辑、Gradle 构建、界面预览、设备模拟、性能分析、调试测试及 APK 分析等工具。

      Xcode macOS版
      Xcode macOS版

      Xcode 是苹果面向 Mac 提供的官方集成开发环境,将代码编辑、项目构建、SwiftUI 实时预览、设备模拟、自动化测试、断点调试、性能分析和应用分发整合在同一套工作流中,适合开发与维护 iPhone、iPad、Mac、Apple Wa

      JetBrains WebStorm macOS版
      JetBrains WebStorm macOS版

      JetBrains WebStorm 是面向 JavaScript、TypeScript 和现代 Web 技术的专业集成开发环境,可智能理解项目结构,提供代码补全、重构、错误检查、运行调试、测试及 Git 工具。

      PhpStorm macOS版
      PhpStorm macOS版

      PhpStorm 是 JetBrains 推出的专业 PHP 集成开发环境,可在 Mac 上完成代码编写、智能检查、重构、调试、测试和数据库管理。它支持主流 PHP 框架、Composer、Git、Docker 与远程解释器。

      JetBrains PyCharm macOS版
      JetBrains PyCharm macOS版

      JetBrains PyCharm 是面向 Python 开发者的集成开发环境,提供智能补全、代码检查、重构、调试、测试、版本控制及虚拟环境管理功能。Mac 版兼容 Intel 与 Apple 芯片,核心功能可免费使用。

      JetBrains IntelliJ IDEA macOS版
      JetBrains IntelliJ IDEA macOS版

      IntelliJ IDEA 是 JetBrains 推出的专业集成开发环境,重点服务于 Java、Kotlin 与 JVM 项目开发。软件集成代码补全、静态检查、重构、调试、测试、版本控制及构建工具,并可借助插件扩展语言和框架支持。

      Visual Studio Code macOS版
      Visual Studio Code macOS版

      Visual Studio Code 是微软提供的免费代码编辑器,支持 macOS,并为 Intel 与 Apple 芯片 Mac 提供适配版本。软件集代码编辑、调试、Git 源代码管理、集成终端和扩展市场于一体。

      REAPER macOS版
      REAPER macOS版

      REAPER是Cockos开发的数字音频工作站,提供多轨音频与MIDI录制、剪辑、处理、混音和母带制作工具。Mac版兼容Intel与Apple芯片,支持AU、VST、VST3、CLAP等插件格式,并提供高度可定制的工作流程。

      Adobe Audition macOS版
      Adobe Audition macOS版

      Adobe Audition 是面向播客、影视后期、音乐制作及内容创作者的专业音频工作站,提供波形精修、多轨录制与混音、频谱编辑、降噪修复、响度匹配和效果处理工具,并可与 Adobe Premiere 协同完成视频声音制作。

      Pro Tools macOS版
      Pro Tools macOS版

      Pro Tools 是 Avid 推出的专业数字音频工作站,覆盖多轨录音、无损剪辑、MIDI 编曲、插件处理、自动化混音及声音与画面同步等工作。它拥有 Intro、Artist、Studio 和 Ultimate 等方案。

      WINDOWS 更多
      PixPix 网页版
      PixPix 网页版

      PixPix 是面向品牌、卖家和内容团队的在线 AI 图片视频创作平台,提供图视频 Agent、无限画布、图片生成、视频生成、自然语言编辑及商品套图、产品精修和带货视频等电商工具。

      Postman Windows版
      Postman Windows版

      Postman 是面向 API 全生命周期的开发与协作平台。Windows 桌面版可用于构建和发送 HTTP、GraphQL、gRPC、WebSocket、MQTT 等请求,并提供环境变量、集合、脚本测试、模拟服务、监控、文档及团队工作区等

      Docker Desktop Windows版
      Docker Desktop Windows版

      Docker Desktop 为 Windows 开发者提供集成式容器环境,内置 Docker Engine、命令行工具、Compose、Build 与 Kubernetes 等组件,可在本机完成镜像构建、容器运行、日志查看和资源管理。

      Android Studio Windows版
      Android Studio Windows版

      Android Studio 是 Google 面向 Android 应用开发提供的官方集成开发环境,以 IntelliJ IDEA 为基础,集成智能代码编辑、Gradle 构建、设备模拟、界面预览、调试测试及性能分析工具,

      JetBrains WebStorm Windows版
      JetBrains WebStorm Windows版

      WebStorm 是 JetBrains 面向 JavaScript、TypeScript 和现代 Web 技术打造的集成开发环境。它提供项目级代码分析、智能补全、安全重构、导航搜索,并集成调试、测试、终端、Git 与常用前端工具,

      JetBrains PhpStorm Windows版
      JetBrains PhpStorm Windows版

      PhpStorm 是 JetBrains 面向 PHP 与现代 Web 开发打造的专业集成开发环境,提供代码补全、静态检查、重构、调试、测试、数据库、版本控制及前端语言支持。Windows 用户可直接下载安装程序,

      JetBrains PyCharm Windows版
      JetBrains PyCharm Windows版

      PyCharm 是 JetBrains 推出的 Python 集成开发环境,提供智能代码补全、错误检查、重构、调试、测试、Git、终端及 Jupyter Notebook 支持。统一版安装后可体验 30 天 Pro 功能,

      JetBrains IntelliJ IDEA Windows版
      JetBrains IntelliJ IDEA Windows版

      JetBrains IntelliJ IDEA 是以 Java、Kotlin 和 JVM 项目为核心的专业集成开发环境,提供智能补全、代码检查、安全重构、调试、测试、版本控制及构建工具集成。统一发行版的核心功能可免费用于商业和非商业项目,

      Visual Studio Code Windows版
      Visual Studio Code Windows版

      Visual Studio Code 是微软推出的免费代码编辑器,Windows 版集成智能补全、代码导航、调试、终端和版本控制,并可通过扩展添加语言、格式化及远程开发能力。它启动较快、配置灵活,

      REAPER Windows版
      REAPER Windows版

      REAPER 是 Cockos 推出的数字音频工作站,适用于录音、MIDI 编曲、音频编辑、混音、母带制作及声音设计。软件安装包小巧,支持多轨录制、灵活路由、自动化、第三方插件和脚本扩展,并提供完整功能的 60 天免费评估期。

      Adobe Audition Windows版
      Adobe Audition Windows版

      Adobe Audition 是面向播客、影视后期、广播与音乐制作的专业音频工作站,提供波形编辑、多轨混音、频谱分析、录音、降噪修复和效果处理等功能。Windows 版通过 Adobe Creative Cloud 安装和管理,

      Pro Tools Windows版
      Pro Tools Windows版

      Pro Tools 是 Avid 推出的专业数字音频工作站,提供多轨录音、音频剪辑、MIDI编曲、插件处理、自动化混音及影视声音制作能力。它适合音乐人、录音师、制作人和音频后期团队使用,并提供免费入门版、订阅方案及永久授权选择。