Java中反射的学习笔记分享

目录
  • 简介
    • 一个简单的例子
    • 设置使用反射
    • 模拟instanceof运算
    • 了解类的方法
    • 获取有关构造函数的信息
    • 查找类字段
    • 按名称调用方法
    • 创建新对象
    • 更改字段的值
    • 使用数组
  • 总结

简介

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

从程序内部检查和操作Java类的能力听起来可能不太显示,但是在其他编程语言中,这个特性根本不存在。例如,在C或C ++ 程序中无法获取有关该程序中定义的函数的信息。

反射的一个具体用途是在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被创建,然后我们检查类实例对象,以查看它们是否是AInteger(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());
               System.out.println("decl class = " +
                              m.getDeclaringClass());
               Class pvec[] = m.getParameterTypes();
               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("return type = " +
                                  m.getReturnType());
               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 [Ljava.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程序中进行操作。此功能非常强大,但是也要谨慎使用

以上就是Java中反射的学习笔记分享的详细内容,更多关于Java反射的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java反射机制介绍

    1.通过反射,我们可以构建实例,得到成员变量的值,得到方法并调用. 还可以获得定义在成员变量.方法.方法参数上的注解. 接下来看代码实现,然后讲原理. 1)构建无参实例:通过反射调用无参构造函数 //1.通过全类名加载字节码对象 Class clazz = Class.forName("com.example.lib.Person"); //2.通过类的字节码拿到定义的构造函数 Constructor constructor = clazz.getConstructor(); //3.

  • Java使用反射获取list泛型过程浅析

    通过属性来获取泛型的类型 Field[] fields = bean.getClass().getDeclaredFields(); for(Field f : fields){ f.setAccessible(true); if(f.getType() == java.util.List.class){ // 如果是List类型,得到其Generic的类型 Type genericType = f.getGenericType(); if(genericType == null) contin

  • Java反射使用的详细介绍(最新推荐)

    目录 反射 反射基本介绍 反射获取类对象 反射获取构造器对象 反射获取成员变量对象 反射获取方法对象 反射 反射基本介绍 反射概述: 反射认为类的每一个成份都是一个对象, 对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分. 在运行时,可以直接得到这个类的构造器对象:Constructor 在运行时,可以直接得到这个类的成员变量对象:Field 在运行时,可以直接得到这个类的成员方法对象:Method 这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语

  • 图文详解Java的反射机制

    目录 1.什么是反射 2.Hello,java反射 3.java程序运行的三个阶段 4.反射相关类 5.反射的优化 6.Class类分析 7.获取Class对象的六种方式 8.类加载机制 动态加载和静态加载 类加载流程概述 加载阶段 连接阶段 初始化 9.通过反射获取类的结构信息 1.什么是反射 反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息. 加载类后,在堆中就产生了一个class类型的对象,这个对象包含了类的完整结构的信息,通过这个对象得到类的结构.这

  • Java中反射的学习笔记分享

    目录 简介 一个简单的例子 设置使用反射 模拟instanceof运算 了解类的方法 获取有关构造函数的信息 查找类字段 按名称调用方法 创建新对象 更改字段的值 使用数组 总结 简介 反射是Java编程语言中的一个特性.它允许执行的Java程序检查或 操作 自身,并操作程序的内部属性.例如,Java类可以获取其所有成员的名称并显示它们. 从程序内部检查和操作Java类的能力听起来可能不太显示,但是在其他编程语言中,这个特性根本不存在.例如,在C或C ++ 程序中无法获取有关该程序中定义的函数的

  • JavaWeb学习笔记分享(必看篇)

    自定义列表 <dl></dl>:表示列表的范围 **在里面 <dt></dt>:上层内容 **在里面 <dd></dd>:下层内容 有序列表 <ol></ol>:有序列表的范围 --属性 type:设置排序方式,1(默认),a,i.. **在ol标签里面 <li>具体内容</li> 无序列表 <ul></ul>:无序列表的范围 --属性 type:circle(空

  • Java中反射机制和作用详解

    前言 很多刚学Java反射的同学可能对反射技术一头雾水,为什么要学习反射,学习反射有什么作用,不用反射,通过new也能创建用户对象. 那么接下来大师就带你们了解一下反射是什么,为什么要学习反射? 下面我们首先通过一个实例来说明反射的好处: 方法1.不用反射技术,创建用户对象,调用sayHello方法 1.1 我们首先创建一个User类 package com.dashi; /** * Author:Java大师 * User对象,包含用户的id和姓名以及sayHello方法 */ public

  • Java中的SPI机制案例分享

    目录 1 简单介绍 2 SPI 案例 3 SPI 的原理剖析 1 简单介绍 当我们封装了一套接口,其它项目想要调用我们的接口只需要引入我们写好的包,但是其它项目如果想要对我们的接口进行扩展,由于接口是被封装在依赖包中的,想要扩展并不容易,这时就需要依赖于Java为我们提供的SPI机制. SPI的全称是Service Provider Interface,服务提供者接口,而与之最接近的概念就是API,全称Application Programming Interface,应用程序编程接口.那么这两

  • 详解java中反射机制(含数组参数)

    详解java中反射机制(含数组参数) java的反射是我一直非常喜欢的地方,因为有了这个,可以让程序的灵活性大大的增加,同时通用性也提高了很多.反射原理什么的,我就不想做过大介绍了,网上一搜,就一大把.(下面我是只附录介绍下) Reflection 是Java被视为动态(或准动态)语言的一个关键性质.这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等).superclass(例如O

  • java中反射和注解的简单使用方法

    目录 什么反射? Java反射机制提供的功能 反射相关的主要API Class 类 获取Class 类的实例( 四种方法) 哪些类型可以有Class 对象? 演示Class类的常用方法 有了Class对象,能做什么? 调用运行时类的指定结构 1. 调用指定方法 关于setAccessible 调用Class对象的newInstance()方法 综合案例: 注解 什么是注解? 常见的Annotation JDK 中的元注解 自定义 Annotation 最后通过反射获取注解信息: 总结 什么反射?

  • JAVA中反射机制和模块化的深入讲解

    目录 一.类加载 1.1类加载描述 1.2类的加载 1.3类的连接 1.4类的初始化 1.4.1类初始化的作用 1.4.2初始化步骤 1.4.3类的初始化时机 二.反射 2.1反射的概述 2.2获取Class类对象的三种方式 2.3反射获取构造方法 2.4反射获取成员变量 2.5反射获取成员方法 2.6反射的案例 2.6.1反射练习之越过泛型检查 2.6.2运行配置文件中指定类的指定方法 三.模块化 3.1模块化概述 ​​​​​​​​​​​​​​3.2模块化使用 3.3模块化的基本使用 总结 一

  • Java中实现代码优化的技巧分享

    目录 1.用String.format拼接字符串 2.创建可缓冲的IO流 3.减少循环次数 4.用完资源记得及时关闭 5.使用池技术 1.用String.format拼接字符串 不知道你有没有拼接过字符串,特别是那种有多个参数,字符串比较长的情况. 比如现在有个需求:要用get请求调用第三方接口,url后需要拼接多个参数. 以前我们的请求地址是这样拼接的: String url = "http://susan.sc.cn?userName="+userName+"&a

  • Golang中反射的常见用法分享

    目录 根据类型做不同处理 标准库 json 中的示例 基本类型的反射 数组类型的反射 chan 反射 map 反射 迭代反射 map 对象 slice 反射 string 反射 interface/Pointer 反射 结构体的反射 遍历结构体字段 根据名称或索引获取结构体字段 修改结构体字段 结构体方法调用 是否实现接口 结构体的 tag 修改结构体未导字段 方法的反射 入参和返回值 通过反射调用方法 总结 在之前的两篇文章 <深入理解 go reflect - 反射基本原理>.<深入

  • Java语法基础之运算符学习笔记分享

    一.运算符 运算符包括下面几种: 算术运算符赋值运算符比较运算符逻辑运算符位运算符三目运算符 最不常用的是位运算符,但也是最接近计算机底层的. 1.算术运算符 (1)+的几种用法:加法.正数.字符串连接符 (2)除法的时候要注意一个问题:整数相除,只能得到整数.要想得到小数,可以将数据自身*1.0,即将数据自身先转换为浮点型. 2.赋值运算符 符号 = += -= *= /= %= 注:=为基本的赋值运算符,其他的为扩展的赋值运算符 面试题: (1)short s=1, s = s+1; (2)

随机推荐