展开菜单
首页 精品内容 本月促销 装机必备 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软件 更多
      讯飞智作 网页版
      讯飞智作 网页版

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      Aperty macOS版
      Aperty macOS版

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      立刻MV 网页版
      立刻MV 网页版

      立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

      蛙蛙写作 网页版
      蛙蛙写作 网页版

      蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

      超级简历WonderCV 网页版
      超级简历WonderCV 网页版

      超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

      音述AI 网页版
      音述AI 网页版

      音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

      AionClaw macOS版
      AionClaw macOS版

      AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

      Loomy macOS版
      Loomy macOS版

      Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

      AirBuddy 3 macOS版
      AirBuddy 3 macOS版

      AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

      AirServer macOS版
      AirServer macOS版

      AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

      AnyDesk macOS版
      AnyDesk macOS版

      AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

      WINDOWS 更多
      讯飞智作 网页版
      讯飞智作 网页版

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      PixTV 网页版
      PixTV 网页版

      PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。

      讯飞绘文 网页版
      讯飞绘文 网页版

      讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

      Maya(Autodesk Maya) Windows版
      Maya(Autodesk Maya) Windows版

      Autodesk Maya 是面向影视、电视、动画和游戏行业的专业三维制作软件,集多边形与 NURBS 建模、角色绑定、关键帧和动作捕捉动画、Bifrost 特效模拟、材质开发及 Arnold 渲染于一体,

      Aperty Windows版
      Aperty Windows版

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      立刻MV 网页版
      立刻MV 网页版

      立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

      蛙蛙写作 网页版
      蛙蛙写作 网页版

      蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

      超级简历WonderCV 网页版
      超级简历WonderCV 网页版

      超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

      音述AI 网页版
      音述AI 网页版

      音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

      AionClaw Windows版
      AionClaw Windows版

      AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

      Loomy Windows版
      Loomy Windows版

      Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。