Java反射学习笔记--使用示例

news2024/11/28 18:50:57

简介

反射是Java编程语言中的一个特性。它允许执行的Java程序 检查 或 操作 自身,并操作程序的内部属性。例如,Java类可以获取其所有成员的名称并显示它们。

反射的一个具体用途是在JavaBeans中,软件组件可以通过一个构建工具进行可视化操作。该工具使用反射来获取Java组件 (类) 动态加载时的属性。

一个简单的例子

要了解反射是如何工作的,请考虑以下简单示例:

import java.lang.reflect.*;

   public class DumpMethods {
      public static void main(String args[])
      {
         try {
            Class c = Class.forName(args[0]);
            Method m[] = c.getDeclaredMethods();
            for (int i = 0; i < m.length; i++)
            System.out.println(m[i].toString());
         }
         catch (Throwable e) {
            System.err.println(e);
         }
      }
   }

对于以下项的调用:

java DumpMethods java.util.Stack

输出为:

public java.lang.Object java.util.Stack.push(

    java.lang.Object)
   public synchronized 
     java.lang.Object java.util.Stack.pop()
   public synchronized
      java.lang.Object java.util.Stack.peek()
   public boolean java.util.Stack.empty()
   public synchronized 
     int java.util.Stack.search(java.lang.Object)

也就是说,类的方法名称java.util.Stack列出了它们及其完全限定的参数和返回类型。

此程序使用加载指定的类 class.forName, 然后调用 getDeclaredMethods 方法检索类中定义的方法列表. java.lang.reflect.Method是表示单个类方法的类。

设置使用反射

反射类,例如Method,在java.lang.反射中找到。使用这些类必须遵循三个步骤。第一步是获得一个java.lang.Class要操作的类的对象。java.lang.Class用于表示正在运行的Java程序中的类和接口。

获取类对象的一种方法是:

Class c = Class.forName("java.lang.String");

上述代码获取的类对象 String.

另一种方法是使用:

Class c = int.class;

或者

Class c = Integer.TYPE;

获取基本类型的类信息。后一种方法访问预定义TYPE 包装类型 (例如Integer) 为基本类型。

第二步是调用方法,例如getDeclaredMethods,以获取该类声明的所有方法的列表。

一旦掌握了这些信息,那么第三步就是使用反射API来操作这些信息。例如:

Class c = Class.forName("java.lang.String"); 
Method m[] = c.getDeclaredMethods(); 
System.out.println(m[0].toString());

在下面的示例中,将三个步骤结合在一起,以呈现如何使用反射处理特定应用的独立插图。

模拟 instanceof 运算

一旦掌握了类信息,下一步通常是询问有关类对象的基本问题。

例如,Class.isInstance方法可以用来模拟instanceof 运算:

class A {}

   public class instance1 {
      public static void main(String args[])
      {
         try {
            Class cls = Class.forName("A");
            boolean b1 
              = cls.isInstance(new Integer(37));
            System.out.println(b1);
            boolean b2 = cls.isInstance(new A());
            System.out.println(b2);
         }
         catch (Throwable e) {
            System.err.println(e);
         }
      }
   }

在此示例中,类对象A被创建,然后我们检查类实例对象,查看它们是否是类对象A

Integer(37)不是,但是new A()是。

了解类的方法

反射最有价值和最基本的用途之一是找出类中定义了哪些方法。为此,可以使用以下代码:

import java.lang.reflect.*;

   public class method1 {
      private int f1(
       Object p, int x) throws NullPointerException
      {
         if (p == null)
            throw new NullPointerException();
         return x;
      }

      public static void main(String args[])
      {
         try {
           Class cls = Class.forName("method1");

            Method methlist[] 
              = cls.getDeclaredMethods();
            for (int i = 0; i < methlist.length;
               i++) {  
               Method m = methlist[i];
               System.out.println("name 
                 = " + m.getName());
               for (int j = 0; j < pvec.length; j++)
                  System.out.println("
                   param #" + j + " " + pvec[j]);
               Class evec[] = m.getExceptionTypes();
               for (int j = 0; j < evec.length; j++)
                  System.out.println("exc #" + j 
                    + " " + evec[j]);
               System.out.println("-----");
            }
         }
         catch (Throwable e) {
            System.err.println(e);
         }
      }
   }

程序首先获取method1的类描述,然后调用getDeclaredMethods(一个用于获取类中定义的每个方法的函数)检索Method 对象列表。这些方法包括public、protect、package和priva。如果你在程序中使用getMethods 而不是getDeclaredMethods,还可以获取继承方法的信息。

程序的输出为:

name = f1
   decl class = class method1
   param #0 class java.lang.Object
   param #1 int
   exc #0 class java.lang.NullPointerException
   return type = int
   -----
   name = main
   decl class = class method1
   param #0 class java.lang.String;
   return type = void
   -----

获取有关构造函数的信息

使用类似的方法来找出类的构造函数。例如:

import java.lang.reflect.*;

   public class constructor1 {
      public constructor1()
      {
      }

      protected constructor1(int i, double d)
      {
      }

      public static void main(String args[])
      {
         try {
           Class cls = Class.forName("constructor1");

           Constructor ctorlist[]
               = cls.getDeclaredConstructors();
         for (int i = 0; i < ctorlist.length; i++) {
               Constructor ct = ctorlist[i];
               System.out.println("name 
                 = " + ct.getName());
               System.out.println("decl class = " +
                            ct.getDeclaringClass());
               Class pvec[] = ct.getParameterTypes();
               for (int j = 0; j < pvec.length; j++)
                  System.out.println("param #" 
                     + j + " " + pvec[j]);
               Class evec[] = ct.getExceptionTypes();
               for (int j = 0; j < evec.length; j++)
                  System.out.println(
                    "exc #" + j + " " + evec[j]);
               System.out.println("-----");
            }
          }
          catch (Throwable e) {
             System.err.println(e);
          }
      }
   }

在此示例中没有检索到返回类型信息,因为构造函数实际上没有真正的返回类型。

运行此程序时,输出为:

name = constructor1
   decl class = class constructor1
   -----
   name = constructor1
   decl class = class constructor1
   param #0 int
   param #1 double
   -----

💦查找类字段

还可以找出类中定义了哪些数据字段。为此,可以使用以下代码:

import java.lang.reflect.*;

   public class field1 {
      private double d;
      public static final int i = 37;
      String s = "testing";

      public static void main(String args[])
      {
         try {
            Class cls = Class.forName("field1");

            Field fieldlist[] 
              = cls.getDeclaredFields();
            for (int i 
              = 0; i < fieldlist.length; i++) {
               Field fld = fieldlist[i];
               System.out.println("name
                  = " + fld.getName());
               System.out.println("decl class = " +
                           fld.getDeclaringClass());
               System.out.println("type
                  = " + fld.getType());
               int mod = fld.getModifiers();
               System.out.println("modifiers =  " + "
                          Modifier.toString(mod));
               System.out.println("-----");
            }
          }
          catch (Throwable e) {
             System.err.println(e);
          }
       }
   }

此示例与前面的示例相似。一个新功能是使用Modifier。这是一个反射类,表示在字段成员上找到的修饰符,例如private int。修饰符本身由整数表示,并且Modifier.toString用于返回默认声明顺序中的字符串表示形式 (例如final之前的static)。程序的输出为:

name = d
   decl class = class field1
   type = double
   modifiers = private
   -----
   name = i
   decl class = class field1
   type = int
   modifiers = public static final
   -----
   name = s
   decl class = class field1
   type = class java.lang.String
   modifiers =
   -----

与方法一样,可以仅获取有关类中声明的字段的信息 (getDeclaredFields),或获取有关超类中定义的字段的信息 (getFields)。

按名称调用方法

到目前为止,已经提出的例子都与获取class有关。但是也可以以其他方式使用反射,例如调用指定名称的方法。

要了解其工作原理,请考虑以下示例:

import java.lang.reflect.*;

   public class method2 {
      public int add(int a, int b)
      {
         return a + b;
      }

      public static void main(String args[])
      {
         try {
           Class cls = Class.forName("method2");
           Class partypes[] = new Class[2];
            partypes[0] = Integer.TYPE;
            partypes[1] = Integer.TYPE;
            Method meth = cls.getMethod(
              "add", partypes);
            method2 methobj = new method2();
            Object arglist[] = new Object[2];
            arglist[0] = new Integer(37);
            arglist[1] = new Integer(47);
            Object retobj 
              = meth.invoke(methobj, arglist);
            Integer retval = (Integer)retobj;
            System.out.println(retval.intValue());
         }
         catch (Throwable e) {
            System.err.println(e);
         }
      }
   }

假设一个程序想要调用add方法,但直到执行时才知道。也就是说,在执行期间指定方法的名称 (例如,这可以由JavaBeans开发环境完成)。上面的程序展示了一种方法。

getMethod用于在类中查找具有两个integer参数类型并具有适当名称的方法。一旦找到此方法并将其捕获到Method 对象,它是在适当类型的对象实例上调用的。要调用方法,必须构造一个参数列表,基本整数值为37和47Integer 对象。返回值 (84) 也被包含在Integer 对象。

创建新对象

构造函数不等同于方法调用,因为调用构造函数等同于创建新对象 (最准确地说,创建新对象涉及内存分配和对象构造)。所以最接近前面例子的是:

import java.lang.reflect.*;

   public class constructor2 {
      public constructor2()
      {
      }

      public constructor2(int a, int b)
      {
         System.out.println(
           "a = " + a + " b = " + b);
      }

      public static void main(String args[])
      {
         try {
           Class cls = Class.forName("constructor2");
           Class partypes[] = new Class[2];
            partypes[0] = Integer.TYPE;
            partypes[1] = Integer.TYPE;
            Constructor ct 
              = cls.getConstructor(partypes);
            Object arglist[] = new Object[2];
            arglist[0] = new Integer(37);
            arglist[1] = new Integer(47);
            Object retobj = ct.newInstance(arglist);
         }
         catch (Throwable e) {
            System.err.println(e);
         }
      }
   }

它查找处理指定参数类型并调用它的构造函数,以创建对象的新实例。这种方法的价值在于它纯粹是动态的,在执行时而不是在编译时使用构造函数查找和调用。

更改字段的值

反射的另一个用途是改变对象中数据字段的值。它的值再次从反射的动态性质中导出,其中可以在执行程序中按名称查找字段,然后更改其值。以下示例说明了这一点:

import java.lang.reflect.*;

   public class field2 {
      public double d;

      public static void main(String args[])
      {
         try {
            Class cls = Class.forName("field2");
            Field fld = cls.getField("d");
            field2 f2obj = new field2();
            System.out.println("d = " + f2obj.d);
            fld.setDouble(f2obj, 12.34);
            System.out.println("d = " + f2obj.d);
         }
         catch (Throwable e) {
            System.err.println(e);
         }
      }
   }

在此示例中,d字段的值设置为12.34。

使用数组

反射的一个用途是创建和操作数组。Java语言中的数组是类的一种特殊类型,并且可以将数组引用分配给Object

要查看数组的工作方式,请考虑以下示例:

import java.lang.reflect.*;

   public class array1 {
      public static void main(String args[])
      {
         try {
            Class cls = Class.forName(
              "java.lang.String");
            Object arr = Array.newInstance(cls, 10);
            Array.set(arr, 5, "this is a test");
            String s = (String)Array.get(arr, 5);
            System.out.println(s);
         }
         catch (Throwable e) {
            System.err.println(e);
         }
      }
   }

此示例创建一个10长的字符串数组,然后将数组中的位置5设置为字符串值。将检索并显示该值。

以下代码说明了对数组的更复杂的操作:

import java.lang.reflect.*;

   public class array2 {
      public static void main(String args[])
      {
         int dims[] = new int[]{5, 10, 15};
         Object arr 
           = Array.newInstance(Integer.TYPE, dims);

         Object arrobj = Array.get(arr, 3);
         Class cls = 
           arrobj.getClass().getComponentType();
         System.out.println(cls);
         arrobj = Array.get(arrobj, 5);
         Array.setInt(arrobj, 10, 37);

         int arrcast[][][] = (int[][][])arr;
         System.out.println(arrcast[3][5][10]);
      }
   }

此示例创建一个5x10x15的int数组,然后继续将数组中的位置 [3][5][10] 设置为值37。请注意,多维数组实际上是数组数组,因此,例如,在第一个array.get之后,arrobj中的结果是10x15数组。再次将其剥离以获得15长的数组,并使用Array.setInt

请注意,创建的数组类型是动态的,不必在编译时知道。

总结

Java反射非常有用,因为它支持按名称动态检索有关类和数据结构的信息,并允许在执行的Java程序中进行操作。此功能非常强大,但是也要谨慎使用

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/26721.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

specCPU 2006 备忘

前言 首先 specCPU是收费的,好像是800还是1000$&#xff0c;缴费了才有软件分发给你&#xff0c;但是个人或者国内某些项目测试都是百度或者找整机&#xff0c;CPU或者操作系统厂家给。 specCPU和其他性能测试工具类似&#xff0c;基本上都是在被测试机器现场编译测试程序&am…

C++11主要新增使用语法介绍

目录 1. C11简介 2. 统一的列表初始化 2.1 &#xff5b;&#xff5d;初始化 2.2 std::initializer_list 3. 声明 3.1 auto 3.2 decltype 3.3 nullptr 4. STL中一些变化 5. 右值引用和移动构造/赋值 5. 1 左值引用和右值引用 5.2 右值引用使用场景和意义 5.3 完美转发…

map底层实现原理

目录一、map数据结构二、bucket数据结构三、哈希冲突四、负载因子五、渐进式扩容1.扩容的前提条件2.增量扩容3.等量扩容六、查找过程七、插入过程八、Map的value赋值九、Map的遍历赋值一、map数据结构 Golang的map使用哈希表作为底层实现&#xff0c;一个哈希表里可以有多个哈…

2022-11-20 C++并发编程( 四十四 ) -- 通讯顺序进程 CSP 范型

C并发编程 -- 四十四 前言一、通讯顺序进程 CSP 范型1. 细节分解1. lambda 封装类成员函数和 std::function< > 函数封装器2. 模板和 dynamic_cast< >类型转换二、ATM 示例总结前言 并发编程除了常规的使用锁, 或无锁结构实现某些并发计算, 还有没有其它实现形式?…

controller-informer

推翻了自己的三次理论&#xff0c;最终确定informer流程。自己梳理&#xff0c;有错请提示&#xff0c;以便及时改正 一、流程图 图1为整体流程&#xff0c;图2位细节流程 二、主要组件 Reflector-负责与API-Server进行绑定Delta FIFO-增量先进先出队列Indexer-本地缓存Resour…

Linux--系统基础磁盘管理相关知识详解笔记

1.磁盘知识体系结构 第一个层次&#xff1a;磁盘相关物理知识 内部结构 外部结构 读写数据原理 第二个层次&#xff1a;磁盘阵列知识 磁盘弹性扩展知识 ​ 阵列&#xff1a;将多块磁盘整合为一块 ​ &#xff08;1&#xff09;可以存储更大容量数据 ​ &#xff08;2&#…

[附源码]java毕业设计心理问题咨询预约系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

docker启动mysql实例之后,docker ps命令查询不到

1.首先拉取mysql,创建并启动实例 #docker pull mysql:5.7 # docker run -p 3306:3306 --name mysql \ -v /mydata/mysql/log:/var/log/mysql \ -v /mydata/mysql/data:/var/lib/mysql \ -v /mydata/mysql/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORDroot \ -d mysql:5.7…

Softing DTS.monaco 9.03新版发布,带有远程接口、并行诊断和动态VCI处理功能

2022年10月24日&#xff0c;Softing推出了“诊断工具集&#xff08;DTS&#xff09;”的新版本——Softing DTS.monaco 9.03。9.03版的诊断测试仪Softing DTS.monaco包含了很多新功能&#xff0c;将使您的工作变得更加简单。由于集成了一个新的连接插件&#xff08;动态VCI处理…

XCZU19EG_FFVC1760芯片的封装和管脚

首先写这篇文章是因为我有接触到这款芯片&#xff0c;但是在做接口约束时&#xff0c;有许多地方并不是很清楚&#xff0c;因此在这里对官方文档(ug1075)进行了通读&#xff0c;只翻译了我觉得我需要用到和需要了解的地方&#xff0c;具体是什么还需要大家去自己阅读官方文档。…

LeetCode 319 周赛

纪念本狗第三次AK&#xff01;&#xff01;&#xff01; 2469. 温度转换 给你一个四舍五入到两位小数的非负浮点数 celsius 来表示温度&#xff0c;以 摄氏度&#xff08;Celsius&#xff09;为单位。 你需要将摄氏度转换为 开氏度&#xff08;Kelvin&#xff09;和 华氏度&a…

RK3568平台开发系列讲解(图像篇)JPEG图像处理

🚀返回专栏总目录 文章目录 一、JPEG文件格式和libjpeg编译二、libjpeg接口函数三、JPEG文件解析沉淀、分享、成长,让自己和他人都能有所收获!😄 📢我们今天来讲解JPEG图像处理。 一、JPEG文件格式和libjpeg编译 JPEG的后缀名为.jpg的图像文件。对于图像内容和信息相同…

Windows安装docker踩坑

安装过程中出现一下问题&#xff0c;步骤如下 菜鸟教程安装windows docker https://www.runoob.com/docker/windows-docker-install.html 启动后报错wsl2错误&#xff0c;因为本机运行的是wsl1&#xff0c;进行解决 wsl -l -v查看运行的虚-了拟机的版本以及状态 因为默认运…

一文带你学习,动态规划算法

一文带你学习&#xff0c;动态规划算法1.什么是动态规划&#xff08;DP&#xff09;2.青蛙跳台阶问题暴力递归解法自顶向下的递归解法自底向上的动态规划解法3.动态规划的解题套路穷举分析确定边界确定最优子结构写出状态转移方程4.经典线性DP&#xff1a;数字三角形5.01背包&a…

金鸡奖提名电影《巴林塔娜》,主题曲和腾格尔合作是未卜先知吗

在刚刚结束的第三十五届金鸡奖上&#xff0c;由十月天传媒推送的电影《巴林塔娜》&#xff0c;获得了最佳提名奖的荣誉。根据资料显示&#xff0c;十月天传媒是来自北京的一家公司&#xff0c;也是国内最具专业的综合性文化产业运营机构。 话说十月天传媒的董事长袁熙伯先生&am…

Qt OpenGL(二十四)——Qt OpenGL 核心模式-实现彩色三角形

Qt OpenGL(二十四)——Qt OpenGL 核心模式-实现彩色三角形 我们之前在Qt OpenGL 核心模式版本中,看到了Qt关于OpenGL的例程,是一个旋转的彩色三角形,本篇文章我们就使用OpenGL核心模式,实现这个彩色三角形。 图1 旋转的三角形 一、彩色三角形 上一篇文章(Qt OpenGL 核心…

儿童护眼灯AA好还是AAA好?双十二精选国AA儿童护眼灯

儿童护眼灯选择国AA级好&#xff01; 这是为什么呢&#xff1f;我给大家分析一下&#xff0c;简单来说&#xff0c;我国对台灯的质量要求是根据《读写作业台灯性能要求》中&#xff0c;当中将分为国A和国AA级两种标准&#xff0c;主要是对照度和会走的均匀度进行划分的&#x…

失物招领|基于Web的校园失物招领系统的设计与实现

作者主页&#xff1a;编程千纸鹤 作者简介&#xff1a;Java、前端、Pythone开发多年&#xff0c;做过高程&#xff0c;项目经理&#xff0c;架构师 主要内容&#xff1a;Java项目开发、毕业设计开发、面试技术整理、最新技术分享 收藏点赞不迷路 关注作者有好处 引言&#xff…

Haproxy负载均衡集群

目录 一、HAPorxy简介 二、HAPorxy优点 三、理解四层和七层负载均衡 四、HAProxy与LVS的区别 五、使用Haproxy搭建web群集 1、部署2台web服务器 2、编译安装Haproxy 3、客户端访问测试 六、总结 1、HTTP请求的两种方式 2、haproxy配置文件重要参数说明 一、HAPorxy简介…

c语言:初识指针

初识指针一.指针是什么问题一&#xff1a;指针&#xff0c;指针变量&#xff0c;内存&#xff0c;地址&#xff0c;编号的关系问题二&#xff1a;为什么指针大小是4或8字节二.如何使用指针三.指针类型问题三&#xff1a;既然指针是4个字节&#xff0c;那它是如何放下long类型的…