爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      第十九章《类的加载与反射》第3节:反射

      首页 知识中心 软件开发 文章详情页

      第十九章《类的加载与反射》第3节:反射

      2023-03-14 10:16:15 阅读次数:497

      Java,反射

      JAVA的反射机制是指在运行状态中,对于任意一个类都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。反射是Java语言中非常重要机制,很多第三方框架都用到了反射,本小节将详细讲解反射机制的原理和作用。

      19.3.1获得Class类对象

      每个类被加载之后,虚拟机就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到这个类。在Java程序中获得Class对象通常有如下三种方式:

      • 使用Class类的forName()静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名,全限定类名包含完整的包名和类名。
      • 调用某个类的class属性来获取该类对应的Class对象。例如,Person.class 将会返回Person类对应的Class对象。
      • 调用某个对象的getClass()方法。该方法是Object 类中的一个方法,所以所有的Java对象都可以调用该方法,该方法将会返回该对象所属类对应的Class对象。

      第一种方式和第二种方式都是直接根据类来取得该类的Class对象。相比之下,第二种方式有如下两种优势。

      • 代码更安全。程序在编译阶段就可以检查需要访问的Class对象是否存在。
      • 程序性能更好。因为这种方式无须调用方法,所以性能更好。

      也就是说,大部分时候都应该使用第二种方式来获取指定类的Class对象。但如果程序只能获得一个字符串,例如“java.lang.String”,如果需要根据字符串来获取对应的Class对象,则只能使用第一种方式,即使用Class的forName()静态方法获取Class对象,该方法可能抛出ClassNotFoundException异常。一旦获得了某个类所对应的Class对象之后,程序就可以调用Class对象的方法来获得该对象和该类的真实信息了。

      19.3.2从Class类对象中获取类的信息

      Class类代表了一个类,而一个类的方法、属性以及所在的包也都可以用类来表示。表示普通方法的类是Method,表示构造方法的类是Constructor,表示属性的类是Field,而表示包的类是Package,只有了解了这些类的意义才能很好的学习Class类。

      Class类提供了大量方法来获取该Class对象所对应类的详细信息,这些方法中很多都包括多个重载的版本,由于重载版本众多,下面的表19-2对每个方法只列出一个版本,读者可以查阅API文档来查看每个方法的详情。

      表19-2 Class类的方法

      方法 功能
      Connstructor getConstructor(Class<?>... parameterTypes) 返回此Class对象对应类的、带指定形参列表的public构造方法。
      Constructor<?>[] getConstructors() 返回此Class对象对应类的所有public构造方法
      Constructor getDeclaredConstructor(Class <?>... parameterTypes) 返回此Class对象对应类的、带指定形参列表的构造方法,与构造方法的访问权限无关。
      Constructor<?>[] getDeclaredConstructors() 返回此Class对象对应类的所有构造方法,与构造方法的访问权限无关
      Method getMethod(String name, Class<?> ... parameterTypes) 返回此Class对象对应类的、带指定形参列表的public方法
      Method[] getMethods() 返回此Class对象所表示的类的所有public方法
      Method getDeclaredMethod(String name, Class<?>.. parameterTypes) 返回此Class对象对应类的、带指定形参列表的方法,与方法的访问权限无关
      Method[] getDeclaredMethods() 返回此Class对象对应类的全部方法,与方法的访问权限无关
      Field getField(String name) 返回此Class对象对应类的、指定名称的public属性
      Field[]getFields() 返回此Class对象对应类的所有public属性
      Field getDeclaredField(String name):
      返回此Class对象对应类的、指定名称的属性,与属性的访问权限无关
      Field[] getDeclaredFields():
      返回此Class对象对应类的全部属性,与属性的访问权限无关
      A getAnnotation(Class annotationClass); 尝试获取该Class对象对应类上存在的、指定类型的注解,如果该类型的注解不存在,则返回null
      A getDeclaredAnnotation(Class annotationClass) 该方法尝试获取直接修饰该Class对象对应类的、指定类型的注解,如果该类型的注解不存在,则返回null
      Annotation[] getAnnotations() 返回修饰该Class对象对应类上存在的所有的注解
      Annotation[] getDeclaredAnnotations() 返回直接修饰该Class对应类的所有注解
      A[] getAnnotationsByType(Class annotationClass) 该方法的功能与getAnnotation()方法基本相似。但由于Java8增加了重复注解功能,因此需要使用该方法获取修饰该类的、指定类型的多个注解
      Class<?>[] getDeclaredClasses() 返回该Class对象对应类里包含的全部内部类
      Class<?> getDeclaringClass() 返回该Class对象对应类所在的外部类
      Class<?>[] getlnterfaces() 返回该Class对象对应类所实现的全部接口
      Class<? super T> getSuperclass() 返回该Class对象对应类的父类的Class对象
      int getModifiers() 返回此类或接口的所有修饰符。修饰符由public、 protected、private、final、static、abstract 等对应的常量组成,返回的整数应使用Modifer工具类的方法来解码,才可以获取真实的修饰符
      Package getPackage() 获取此类的包
      String getName() 以字符串 形式返回此Class对象所表示的类的名称
      String getSimpleName() 以字符串形式返回此Class对象所表示的类的简称,即不包含包名,只有类名
      boolean isAnnotation()。:。 返回此Class对象是否表示一个注解类型
      boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 判断此Class 对象是否使用了Annotation修饰
      boolean isAnonymousClass() 返回此Class对象是否是一个匿 名类
      boolean isArray() 返回此Class对象是否表示一个数组类
      boolean isEnum() 返回此Class对象是否表示一个枚举
      boolean isInterface 返回此Class对象是否表示一个接口
      boolean islnstance(Object obj) 判断obj是否是此Class 对象的实例,该方法与instanceof操作符作用相同

      表19-2中,getMethod0方法和getConstructor()方法都需要传入多个类型为Class<?>的参数,这些参数用于获取指定的方法或指定的构造方法。假设某个类内包含如下三个版本的info()方法:

      • public void info()
      • public void info(String str)
      • public void info(String str , Integer num)

      这三个同名方法属于重载,它们的方法名相同,但参数列表不同。在Java语言中要确定一个方法

      光有方法名是不行的,如果仅仅只指定info()方法,实际上可以是上面三个方法中的任意一一个。如果需要确定一个方法,则应该由方法名和参数列表来确定,但参数名没有任何实际意义,所以只能由参数类型来确定。例如想指定第二个info()方法,则必须指定方法名为info,形参列表为String.class。因此在程序中获取第二个info()方法使用如下代码

      //前一个参数指定方法名,后面的个数可变的Class参数指定参数类型列表​
      clazz. getMethod("info",String.class)​
      

      如果需要获取第三个info()方法,则使用如下代码:

      //前一个参数指定方法名,后面的个数可变的Class参数指定形参类型列表​
      clazz . getMethod("info", String.class, Integer.class)​
      

      获取构造方法时无须传入构造方法名称,因为同一个类的所有构造方法的名字都是相同的,所以要确定一个构造方法只要指定参数列表即可。下面的【例19_06】展示了如何通过Class类对象获得类的信息。

      【例19_06使用Class类对象获取类的信息】

      Exam19_06.java

      import java.lang.annotation.*;
      import java.lang.reflect.*;
      import java.util.Arrays;
      //定义可重复注解
      @Repeatable (Annos.class)
      @interface Anno { }
      @Retention (value=RetentionPolicy.RUNTIME)
      @interface Annos {
          Anno[] value();
      }
      //使用4个注解修饰该类
      @ SuppressWarnings (value="unchecked")
      @ Deprecated
      //使用重复注解修饰该类
      @Anno
      @Anno
      
      class ClassTest{
          //为该类定义一个私有的构造方法
          private ClassTest (){
      
          }
          //定义一个有参数的构造方法
          public ClassTest (String name){
              System.out.println ("执行有参数的构造方法");
          }
          //定义一个无参数的info方法
          public void info(){
              System.out.println( "执行无参数的info方法");
          }
          //定义一个有参数的info方法
          public void info(String str){
              System.out.println("执行有参数的info方法" + ",其str参数值:"+ str);
          }
          //定义一个测试用的内部类
          class Inner{}
      }
      public class Exam19_06 {
          public static void main(String[] args) throws  Exception{
              //下面代码可以获取ClassTest对应的Class
              Class<ClassTest> clazz = ClassTest.class;
              //获取该Class对象所对应类的全部构造方法
              Constructor[] ctors = clazz . getDeclaredConstructors ();
              System.out.println ("ClassTest的全部构造方法如下: ");
              for (Constructor c : ctors)
                  System.out.println(c);
              //获取该Class对象所对应类的全部public构造方法
              Constructor[] publicCtors = clazz. getConstructors();
              System.out.println ("ClassTest的全部public构造方法如下: ");
              for(Constructor c : publicCtors){
                  System.out.println(c);
              }
              //获取该Class对象所对应类的全部public方法
              Method[] mtds = clazz .getMethods();
              System.out.println ("ClassTest的全部public方法如下: ");
              for (Method md : mtds ){
                  System.out.println (md);
              }
              //获取该Class对象所对应类的指定方法
              System.out.println("ClassTest里带一个字符串参数的info方法为:"+clazz.getMethod("info",String.class));
              //获取该Class对象所对应类的全部注解
              Annotation[] anns = clazz.getAnnotations();
              System.out.println ("ClassTest的全部Annotation如下: ");
              for(Annotation an : anns){
                  System.out.println(an);
              }
              System.out.println ("该Class元素上的@SuppressWarnings注解为: "+ Arrays.toString(clazz.getAnnotationsByType(SuppressWarnings.class)));
              System.out.println("该Class元素上的@Anno注解为: " + Arrays.toString (clazz.getAnnotationsByType(Anno.class)));
              //获取该Class对象所对应类的全部内部类
              Class<?>[] inners = clazz.getDeclaredClasses();
              System.out.println ("ClassTest的全部内部类如下: ");
              for (Class c : inners){
                  System. out.println(c);
              }
              //使用Class. forName ()方法加载ClassTest的Inner内部类
              Class inClazz = Class.forName ("ClassTest$Inner");
              //通过getDeclaringClass ()访问该类所在的外部类
              System.out.println ("inClazz对应类的外部类为: " +inClazz.getDeclaringClass());
              System.out.println ("ClassTest的包为: " + clazz.getPackage());
              System.out.println ("ClassTest的父类为: "+ clazz.getSuperclass());
          }
      }
      

      【例19_06】中Annotation表示注解,关于注解的知识将在19.4小节讲解。【例19_06】的运行结果如图19-6所示。

      第十九章《类的加载与反射》第3节:反射

      图19-6【例19_06】运行结果

      从图19-6可以看出:getMethods()方法不仅仅能够获得这个类中定义的方法,还可以获得这个类从父类中继承过来的方法。需要注意:虽然定义ClassTest类时使用了@SuppressWarnings注解,但程序运行时无法分析出该类里包含的该注解,这是因为@SuppressWarmings使用了@Retention(value=SOURCE)修饰,这表明@SuppesWarnings只能保存在源代码级别上,而通过ClassTest.class获取该类的运行时Class对象,所以程序无法访问到@SuppressWarnings注解。

      19.3.3方法参数反射

      Java 8在java.lang.reflect包下新增了一个Executable抽象类,这个类代表所有方法,它有两个子类,分别是代表构造方法的Constructor和代表普通方法的Method。Executable类提供了大量方法来获取修饰该方法的注解信息,还提供了isVarArgs()方法用于判断该方法是否包含数量可变的形式参数,以及通过getModifiers()方法来获取该方法的修饰符。除此之外,Executable提供了如表19-3所示的两个方法来获取该方法的参数个数及参数名称。

      表19-3 Executable类获取参数个数及参数名称的方法

      方法 功能
      int getParameterCount() 获取该方法的参数个数
      Parameter[] getParameters() 获取该方法的所有参数

      表19-3中,Parameter类就表示方法的形式参数。Parameter也提供了大量方法来获取声明该参数的各项信息,如表19-4所示。

      表19-4 Parameter类的常用方法

      方法 功能
      getModifiers() 获取修饰该参数的修饰符
      String getName() 获取参数名称
      Type getParameterizedType() 获取带泛型的参数类型
      Class<?> getType() 获取参数类型
      boolean isNamePresent() 判断该方法返回该类的class文件中是否包含了方法的参数名称信息。
      boolean isVarArgs() 判断该参数是否为个数可变的形参

      需要指出:IDEA编译Java 源文件时,默认生成的class文件并不包含方法的参数名称信息,因此调用isNamePresent()方法将会返回false,调用getName0方法也不能得到该参数的形参名。如果希望编译Java源文件时可以保留参数名称信息,需要对IDEA进行编译参数的设置,具体步骤是:以打开“File”菜单,选择“Settings”菜单项,在弹出的对话框中按照“Build,Execution,Deployment”->“Compiler”->“Java Compiler”的顺序选择选项,并在“Additional command line parameters”后面填上“-parameters”,如图19-7所示。

      第十九章《类的加载与反射》第3节:反射

      图19-7 设置编译参数

      下面的【例19_07】展示了方法参数反射实现过程。

      【例19_07方法参数反射】

      Exam19_07.java

      import java.util.*;
      import java.lang.reflect.*;
      class ParameterTest {
          public void rep(String str,List<String> list){}
      }
      public class Exam19_07 {
          public static void main(String[] args)throws Exception {
              //获取String的类
              Class<ParameterTest> clazz = ParameterTest.class;
              //获取String类的带两个参数的replace()方法
              Method rep = clazz.getMethod("rep", String.class, List.class);
              //获取指定方法的参数个数
              System.out.println("rep()方法参数个数: " + rep.getParameterCount ());
              //获取replace的所有参数信息
              Parameter[] parameters = rep.getParameters();
              int index = 1;
              //遍历所有参数
              for (Parameter  p : parameters) {
                  if (p.isNamePresent()) {
                      System.out.println("---第" + index++ + "个参数信息---");
                      System.out.println("参数名: " + p.getName());
                      System.out.println("形参类型: " + p.getType());
                      System.out.println("泛型类型:" + p.getParameterizedType());
                  }
              }
          }
      }
      

      如果希望能够正确运行【例19_07】,就必须对IDEA设置编译参数,设置完成后还需重新编译项目,具体做法是:打开“菜单”,单击“Rebuild Project”菜单项。完成这一步操作后,运行【例19_07】的结果如图19-8所示。

      第十九章《类的加载与反射》第3节:反射

      图19-8【例19_07】运行结果

      19.3.4利用反射生成并操作对象

      一个Class类对象就代表一个类,程序员不仅仅能够通过这个Class类对象获得对应类的信息,还能通过这个Class类对象创建出对应类的对象。这句话听起来有点绕口,它的意思是:如果一个Class类对象clazz代表A类,那么程序员能够通过clazz来创建出一个A类对象。19.3.3小节曾介绍过:Constructor类代表类的构造方法,而通过反射的方式来生成对象需要先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的对象。下面的【例19_08】展示了使用反射方式创建对象的过程。

      【例19_08 用反射技术创建对象1】

      Exam19_08.java

      import java.util.*;
      class ObjectPoolFactory{
          String[] classNames;//存放类名称的字符串数组
          //定义一个Map类集合,Map的key是对象名,value是实际对象
          Map<String ,Object> objectPool = new HashMap<String ,Object>() ;
          //构造方法
          public ObjectPoolFactory(String[] classNames){
              this.classNames = classNames;
          }
          //createObject()方法根据String型参数clazzName生成Java对象
          private Object createObject (String clazzName) throws Exception {
              //根据字符串来获取对应的Class对象
              Class<?> clazz = Class.forName (clazzName) ;
              //使用clazz对应类的默认构造器创建实例
              return clazz. getConstructor().newInstance() ;
          }
          //initPool()方法根据一组类的名称生成相应的对象并存入objectPool
          public Map<String ,Object> initPool() throws Exception {
              if(classNames!=null){
                  for (int i=0;i< classNames.length;i++){
                      Object o = createObject(classNames[i]);//根据类名创建对象
                      objectPool.put(classNames[i],o);//把类名和创建出的对象存入objectPool
                  }
              }
              return objectPool;
          }
      }
      
      public class Exam19_08 {
          public static void main(String[] args) throws Exception {
              String[] className = {"java.text.SimpleDateFormat","java.util.Date"};
              ObjectPoolFactory opf = new ObjectPoolFactory(className);
              Map<String,Object> objectPool = opf.initPool();
              for (int i=0;i< objectPool.size();i++){//循环取出objectPool中的对象
                  System.out.println(objectPool.get(className[i]));
              }
          }
      }
      

      【例19_08】中定义了一个ObjectPoolFactory类,这个类的createObject()方法可以根据参数指定的类名称通过反射方式创建出该类的对象,而initPool()根据一组类的名称生成一组对象并存入Map集合中。main()方法创建了ObjectPoolFactory类对象并调用其initPool()方法生成一组对象并循环打印这些对象。【例19_08】的运行结果如图19-9所示。

      第十九章《类的加载与反射》第3节:反射

      图19-9【例19_08】运行结果

      需要注意:利用反射方式生成的第二个对象是Date类对象,而Date类无参数构造方法所创建的对象表示当前时间,因此读者的运行结果中Date类对象的打印结果可能与图19-9不同。

      【例19_08】中调用的是类的无参数构造方法创建的对象,如果希望调用有参数的构造方法创建对象,则需要调用有参数的getConstructor()方法来获得Constructor对象。有参数的getConstructor()方法的参数类型是Class,这个参数表示对应类构造方法的参数,而获得的Constructor对象则是对应类的带参数构造方法。例如A类有一个带有参数的构造方法,这个构造方法的参数是String类型,那么通过Class类的有参数的getConstructor()就能获得这个有参数的构造方法,在调用getConstructor()时需要以String.class作为getConstructor()方法的参数。当获得了表示有参数构造方法的Constructor对象后,再调用其newInstance()方法就能以A类有参数的构造方法创建出一个A类对象,并且在调用newInstance()方法时需要向它传递相应的构造方法参数。下面的【例19_09】展示了如何用反射的方式调用到java.util.Date类的有long型参数的构造方法。

      【例19_09 用反射技术创建对象2】

      Exam19_09.java

      import java.lang.reflect.*;
      public class Exam19_09 {
          public static void main(String[] args) throws Exception {
              //获取到表示java.util.Date类的Class对象
              Class clazz = java.util.Date.class;
              //获取到java.util.Date类的以long为参数类型的构造方法
              Constructor constructor = clazz.getConstructor(long.class);//①
              //以long型参数的构造方法创建对象
              Object o = constructor.newInstance(9876543210L);
              System.out.println(o);
          }
      }
      

      【例19_09】中,语句①调用了有参数的getConstructor()方法来获得Date类有参数的构造方法,由于为getConstructor()方法传递的参数是“long.class”,因此获得的Constructor对象表示的是“Date(long date)”这个构造方法。同样,在调用Constructor对象的newInstance()方法时,也需要传递一个long型的参数,这相当于为“Date(long date)”这个构造方法传递了构造参数。此外还需强调,Java语言中,基础数据类型也可以通过“.class”来获得其对应类型的Class对象,并且“long.class”与“Long.class”所得到的Class对象并不是同一个对象。【例19_09】的运行结果如图19-10所示。

      第十九章《类的加载与反射》第3节:反射

      图19-10【例19_09】运行结果

      通过反射的方式不仅能创建出一个类的对象,还能调用到一个类中的方法。如果想通过反射方式调用一个类的方法,先要调用Class的getMethods()方法或getMethod()方法来获取全部方法或指定方法,这两个方法的返回值是Method数组和Method对象。前文介绍过:每个Method对象对应一个方法,获得Method对象后,程序就可通过该Method来调用它对应的方法。调用的具体方式是:在程序中执行Method类的invoke()方法,需要指出:invoke()方法在执行时需要一个对象作为参数,执行invoke()方法就相当于调用了这个对象相应的方法。下面的【例19_10】展示了使用反射方式调用类方法的过程。

      【例19_10用反射技术调用类中的方法】

      Exam19_10.java

      import java.lang.reflect.*;
      public class Exam19_10 {
          public static void main(String[] args) throws Exception {
              Class clazz = System.out.getClass();
              //获取表示println()方法的Method对象
              Method method = clazz.getMethod("println",String.class);
              //通过invoke()方法执行System.out的println()方法
              method.invoke(System.out,"这是通过反射方式打印的字符串");
          }
      }
      

      【例19_10】的语句①通过Method对象的invoke()方法调用了System.out的println()方法,而“这是通过反射方式打印的字符串”则是传递给println()方法的参数。【例19_10】的运行结果如图19-11所示。

      第十九章《类的加载与反射》第3节:反射

      图19-11【例19_10】运行结果

      通过反射的方式还可以访问类的属性。程序员只需要通过Class类的getFields()或getField()方法可以获取该类所包括的全部属性或指定的属性。Java语言中,属性由Field类表示,Field类提供了一组getXxx()方法来获取对象的某个属性的值,而此处的Xxx对应8种基础数据类型,如果该属性的类型是引用类型,则取消get后面的Xxx。此外,Field类还提供了一组setXxx()方法为对象的属性设置成值,此处的Xxx也对应8种基础数据类型,同样如果该属性的类型是引用类型则取消set后面的Xxx。下面的【例19_11】演示了如何使用反射技术操作属性。

      【例19_11用反射技术访问属性】

      Exam19_11.java

      import java.lang.reflect.*;
      class Person{
          private String name;
          private int age;
          public String toString(){
              return " Person[name:" + name +"age:"+age+"]";
          }
      }
      public class Exam19_11 {
          public static void main(String[] args) throws Exception{
              //创建-一个Person对象
              Person p = new  Person() ;
              //获取Person类对应的Class对象
              Class<Person> personClazz = Person.class;
              //获取Person的名为name的属性
              //使用getDeclaredField ()方法表明可获取各种访问控制符的属性
              Field nameField = personClazz .getDeclaredField ("name") ;
              //设置通过反射访问该属性时取消访问权限检查
              nameField. setAccessible (true) ;
              //调用set()方法为P对象的name属性设置值
              nameField.set(p,"张三") ;
              //获取Person类名为age的属性
              Field ageField = personClazz . getDeclaredField("age") ;
              //设置通过反射访问该属性时取消访问权限检查
              ageField.setAccessible (true) ;
              //调用setInt()方法为p对象的age属性设置值
              ageField. setInt(p,20) ;
              System. out.println(p) ;
          }
      }
      

      【例19_11】中定义了一个Person类,该类里包含两个private属性:name和age,在通常情况下,这两个属性只能在Person 类里访问。但main()方法中通过反射修改了Person对象的name、age两个属性的值。语句①使用getDeclaredField()方法获取了名为name 的属性,注意此处不是使用getField()方法,因为getField()方法只能获取public的属性,而getDeclaredField()方法则可以获取所有的属性。语句②是设置访问该属性时不受访问权限的控制,语句③修改了Person对象的name属性的值。修改Person对象的age属性值的方式与修改name属性的方式完全相同。【例19_11】的运行结果如图19-12所示。

      第十九章《类的加载与反射》第3节:反射

      图19-12【例19_11】运行结果

      19.3.5利用反射操作数组

      在Java语言中,数组也可以用反射的方式生成并进行操作。在java.lang.reflect包下提供了一个Array类,Array对象可以代表所有的数组。程序员可以通过使用Array类动态地创建数组,操作数组元素等。Array类所提供的操作数组常用方法如表19-5所示。

      表19-5 Array类操作数组的方法

      方法 功能
      static Object newInstance(Class<?> componentType, int... length) 创建一个具有指定的元素类型、指定维度的新数组
      static xxxgetXxx(Object array, int index) 返回array 数组中第index个元素。其中xxx是各种基础数据类型,如果数组元素是引用类型,则该方法变为get(Object array, int index)
      static void setXxx(Object array, int index, xxx val) 将array数组中第index 个元素的值设为val,其中xxx是各种基础数据类型,如果数组元素是引用类型,则该方法变成set(Object array, intindex, Object val)

      下面的【例19_12】展示了使用Array类创建并操作数组的过程。

      【例19_12 Array类操作数组1】

      Exam19_12.java

      import java.lang.reflect.Array;
      public class Exam19_12 {
          public static void main(String[] args) {
              //创建 一个元素类型为String, 长度为10的数组
              Object arr = Array.newInstance (String. class, 10);
              //依次为arr数组中index为5、6的元素赋值
              Array.set(arr,5,"《Java语言入门》");
              Array.set(arr,6,"《数据库基础》");
              //依次取出arr数组中index为5、6的元素的值
              Object book1 = Array .get (arr,5);
              Object book2 = Array.get (arr,6);
              //输出arr数组中index为5、6的元素
              System. out .println (book1) ;
              System. out .println (book2) ;
          }
      }
      

      【例19_12】的运行结果如图19-13所示。

      第十九章《类的加载与反射》第3节:反射

      图19-13【例19_12】运行结果

      【例19_12】创建并操作了一个一维数组,而Array实际上也能创建并操作多维数组,下面的【例19_13】展示了如何使用Array类创建并操作三维数组。在3.2小节曾经介绍过:多维数组实际上也是一维数组,例如三维数组可以看作是由多个二维数组组成的一维数组,因此可以用操作一维数组的方式来操作二维、三维或更高维度的数组。

      【例19_13 Array类操作数组2】

      Exam19_13.java

      import java.lang.reflect.Array;
      public class Exam19_13 {
          public static void main(String[] args) {
              //创建一个String型三维数组
              Object arr = Array.newInstance (String.class, 3,4,10) ;
              //获取arr数组中index为2的元素,该元素应该是二维数组
              Object arrObj = Array .get(arr, 2);
              //使用Array为二维数组的数组元素赋值,二维数组的数组元素是一维数组
              //所以传入Array的set()方法的第三个参数是一 个一维数组
              Array.set (arrObj,2,new String []{"《Java语言入门》", "《数据库基础》"}) ;
              //获取arrObj数组中index为3的元素,该元素应该是一维数组
              Object anArr = Array.get (arrObj, 3) ;
              Array. set (anArr,8,"《Android开发教程》");
              //将arr强制类型转换为三维数组
              String[][] [] cast = (String[][][])arr;
              //获取cast三维数组中指定元素的值
              System.out.println(cast[2][3][8]) ;
              System.out.println(cast[2][2][0]) ;
              System.out.println(cast[2][2][1]) ;
          }
      }
      

      【例19_13】创建并操作了一个三维数组,操作的每一步都添加了详细的注释,读者可以根据注释理解每一步的操作意义。【例19_13】的运行结果如图19-14所示。

      第十九章《类的加载与反射》第3节:反射

      图19-14【例19_13】运行结果

      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://blog.51cto.com/mugexuetang/5985140,作者:穆哥学堂,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:第二十章《Java Swing》第3节:布局管理器

      下一篇:c++入门篇之C++ 信号处理

      相关文章

      2025-05-14 10:03:13

      arm架构下JAVA开发

      ARM(Advanced RISC Machine)是一种基于精简指令集计算(RISC)设计的处理器架构。它以高效、节能著称,因此广泛应用 于从智能手机到物联网设备的各个领域。

      2025-05-14 10:03:13
      Java , JVM , 嵌入式 , 架构 , 设备
      2025-05-14 10:02:58

      Java实现根据概率中奖率怎么算

      要实现根据概率计算中奖率的功能,可以使用 Java 编程语言编写一个简单的程序。

      2025-05-14 10:02:58
      Java , 概率 , 模拟 , 程序
      2025-05-14 09:51:21

      Java 代码本地设置Hadoop用户名密码

      在Hadoop环境中,通常使用Kerberos进行身份验证。但在一些开发或测试环境中,我们可能需要在本地代码中设置用户名和密码来模拟或进行简单的测试。

      2025-05-14 09:51:21
      Hadoop , Java , 代码 , 使用 , 用户名 , 认证
      2025-05-14 09:51:21

      java 判断map为null或者空

      java 判断map为null或者空

      2025-05-14 09:51:21
      Java , Map , null , 方法 , 是否 , 检查 , 示例
      2025-05-14 09:51:15

      java怎么对线程池做监控

      对Java线程池进行监控是确保系统性能和稳定性的重要部分。监控线程池可以帮助我们了解线程池的状态,如当前活跃线程数、任务队列长度、已完成任务数等。

      2025-05-14 09:51:15
      Java , 方法 , 监控 , 示例 , 线程 , 队列
      2025-05-13 09:53:23

      java动态获取实体类的字段

      在Java中,我们可以使用反射(Reflection)API来动态地获取实体类的字段。

      2025-05-13 09:53:23
      API , Java , 使用 , 字段 , 实体类 , 方法 , 获取
      2025-05-13 09:53:23

      Java静态变量在静态方法内部无法改变值

      在Java中,静态变量(也称为类变量)属于类本身,而不是类的任何特定实例。它们可以在没有创建类的实例的情况下访问和修改。如果我们发现在静态方法内部无法改变静态变量的值,这通常是因为我们的代码中有一些逻辑错误或误解。

      2025-05-13 09:53:23
      Java , 变量 , 实例 , 类名 , 访问 , 静态 , 静态方法
      2025-05-12 10:19:12

      springboot学习(2)

      springboot学习(2)

      2025-05-12 10:19:12
      Java , main , springboot , web , 启动 , 方法 , 浏览器
      2025-05-08 09:03:21

      基于spring+jsp+mysql实现的Java web论坛系统【源码+数据库+指导运行】

      本项目是一套基于spring+jsp+mysql实现的Java web论坛系统,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。

      2025-05-08 09:03:21
      Java , jsp , spring , 功能 , 源码
      2025-05-07 09:08:42

      Java中的异常体系

      Java中的异常体系

      2025-05-07 09:08:42
      Exception , Java , 异常 , 程序
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5220512

      查看更多

      最新文章

      arm架构下JAVA开发

      2025-05-14 10:03:13

      Java实现根据概率中奖率怎么算

      2025-05-14 10:02:58

      Java 代码本地设置Hadoop用户名密码

      2025-05-14 09:51:21

      java 判断map为null或者空

      2025-05-14 09:51:21

      java怎么对线程池做监控

      2025-05-14 09:51:15

      java动态获取实体类的字段

      2025-05-13 09:53:23

      查看更多

      热门文章

      Java线程同步synchronized wait notifyAll

      2023-04-18 14:15:05

      Java/Android Annotation注解/注入(二)

      2023-04-13 09:37:00

      Android/Java判断字符串String是否为float浮点数或double类型

      2023-04-17 09:39:54

      Java小数点数字和百分号数字之间的转换

      2023-04-13 09:48:57

      Java的自带注解Annotation(一)

      2023-05-10 06:02:06

      Java/Android Annotation注解/注入(三)

      2023-04-13 09:37:00

      查看更多

      热门标签

      java Java python 编程开发 代码 开发语言 算法 线程 Python html 数组 C++ 元素 javascript c++
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      深入理解Java中的多线程编程

      初学Java,参数传递无引用传递(七)

      深入理解Java中的反射机制

      Java中的集合类

      如何在Java中实现大规模数据可视化

      Java9系列第三篇-同一个Jar支持多JDK版本运行

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号