Java中反射的学习笔记分享

作者:Zeuss 时间:2021-12-18 14:41:43 

简介

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

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

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

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

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

来源:https://juejin.cn/post/7163089430792634398

标签:Java,反射
0
投稿

猜你喜欢

  • Java版的7种单例模式写法示例

    2023-08-13 04:24:57
  • java中如何截取字符串最后一位

    2023-11-27 00:51:16
  • C++实现LeetCode(9.验证回文数字)

    2023-06-21 00:20:38
  • Java并发编程之线程之间的共享和协作

    2021-07-20 14:28:27
  • JAVA判断空值方法原理解析

    2021-09-28 14:37:34
  • Java对象的XML序列化与反序列化实例解析

    2023-02-25 15:21:19
  • Java设计模式之状态模式

    2022-05-08 07:24:25
  • Spring AOP 后置通知修改响应httpstatus方式

    2022-06-05 06:54:26
  • c#操作ftp类分享

    2023-02-27 12:50:17
  • java封装前端查询条件通用版

    2023-06-24 12:06:15
  • Studio 编译报错:compileSdkVersion 'android-24' requires JDK 1.8 or later to compile.的解决办法

    2023-06-19 17:19:41
  • Spring的refresh()方法相关异常解析

    2021-12-08 07:39:07
  • Mybatis接口式编程的原理

    2023-11-27 22:16:05
  • 专属于程序员的浪漫-Java输出动态闪图iloveyou

    2023-12-01 16:00:57
  • C# Newtonsoft.Json 解析多嵌套json 进行反序列化的实例

    2022-04-09 11:23:13
  • Android带进度条的文件上传示例(使用AsyncTask异步任务)

    2023-06-24 09:43:11
  • 深入浅析Mybatis的缺陷问题

    2023-07-19 19:39:46
  • javac final变量未赋值检测案例讲解

    2023-09-29 04:25:17
  • Struts2拦截器Interceptor的原理与配置实例详解

    2022-06-23 17:34:45
  • Spring Aop 如何获取参数名参数值

    2022-09-08 17:00:41
  • asp之家 软件编程 m.aspxhome.com