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


    简介

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

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

    一个简单的例子

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

    1. import java.lang.reflect.*;
    2. public class DumpMethods {
    3. public static void main(String args[])
    4. {
    5. try {
    6. Class c = Class.forName(args[0]);
    7. Method m[] = c.getDeclaredMethods();
    8. for (int i = 0; i < m.length; i++)
    9. System.out.println(m[i].toString());
    10. }
    11. catch (Throwable e) {
    12. System.err.println(e);
    13. }
    14. }
    15. }

    对于以下项的调用:

    1. java DumpMethods java.util.Stack

    输出为:

    1. public java.lang.Object java.util.Stack.push(
    2. java.lang.Object)
    3. public synchronized
    4. java.lang.Object java.util.Stack.pop()
    5. public synchronized
    6. java.lang.Object java.util.Stack.peek()
    7. public boolean java.util.Stack.empty()
    8. public synchronized
    9. 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来操作这些信息。例如:

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

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

    模拟 instanceof 运算

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

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

    1. class A {}
    2. public class instance1 {
    3. public static void main(String args[])
    4. {
    5. try {
    6. Class cls = Class.forName("A");
    7. boolean b1
    8. = cls.isInstance(new Integer(37));
    9. System.out.println(b1);
    10. boolean b2 = cls.isInstance(new A());
    11. System.out.println(b2);
    12. }
    13. catch (Throwable e) {
    14. System.err.println(e);
    15. }
    16. }
    17. }

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

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

    了解类的方法

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

    1. import java.lang.reflect.*;
    2. public class method1 {
    3. private int f1(
    4. Object p, int x) throws NullPointerException
    5. {
    6. if (p == null)
    7. throw new NullPointerException();
    8. return x;
    9. }
    10. public static void main(String args[])
    11. {
    12. try {
    13. Class cls = Class.forName("method1");
    14. Method methlist[]
    15. = cls.getDeclaredMethods();
    16. for (int i = 0; i < methlist.length;
    17. i++) {
    18. Method m = methlist[i];
    19. System.out.println("name
    20. = " + m.getName());
    21. for (int j = 0; j < pvec.length; j++)
    22. System.out.println("
    23. param #" + j + " " + pvec[j]);
    24. Class evec[] = m.getExceptionTypes();
    25. for (int j = 0; j < evec.length; j++)
    26. System.out.println("exc #" + j
    27. + " " + evec[j]);
    28. System.out.println("-----");
    29. }
    30. }
    31. catch (Throwable e) {
    32. System.err.println(e);
    33. }
    34. }
    35. }

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

    程序的输出为:

    1. name = f1
    2. decl class = class method1
    3. param #0 class java.lang.Object
    4. param #1 int
    5. exc #0 class java.lang.NullPointerException
    6. return type = int
    7. -----
    8. name = main
    9. decl class = class method1
    10. param #0 class java.lang.String;
    11. return type = void
    12. -----

    获取有关构造函数的信息

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

    1. import java.lang.reflect.*;
    2. public class constructor1 {
    3. public constructor1()
    4. {
    5. }
    6. protected constructor1(int i, double d)
    7. {
    8. }
    9. public static void main(String args[])
    10. {
    11. try {
    12. Class cls = Class.forName("constructor1");
    13. Constructor ctorlist[]
    14. = cls.getDeclaredConstructors();
    15. for (int i = 0; i < ctorlist.length; i++) {
    16. Constructor ct = ctorlist[i];
    17. System.out.println("name
    18. = " + ct.getName());
    19. System.out.println("decl class = " +
    20. ct.getDeclaringClass());
    21. Class pvec[] = ct.getParameterTypes();
    22. for (int j = 0; j < pvec.length; j++)
    23. System.out.println("param #"
    24. + j + " " + pvec[j]);
    25. Class evec[] = ct.getExceptionTypes();
    26. for (int j = 0; j < evec.length; j++)
    27. System.out.println(
    28. "exc #" + j + " " + evec[j]);
    29. System.out.println("-----");
    30. }
    31. }
    32. catch (Throwable e) {
    33. System.err.println(e);
    34. }
    35. }
    36. }

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

    运行此程序时,输出为:

    1. name = constructor1
    2. decl class = class constructor1
    3. -----
    4. name = constructor1
    5. decl class = class constructor1
    6. param #0 int
    7. param #1 double
    8. -----

    💦查找类字段

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

    1. import java.lang.reflect.*;
    2. public class field1 {
    3. private double d;
    4. public static final int i = 37;
    5. String s = "testing";
    6. public static void main(String args[])
    7. {
    8. try {
    9. Class cls = Class.forName("field1");
    10. Field fieldlist[]
    11. = cls.getDeclaredFields();
    12. for (int i
    13. = 0; i < fieldlist.length; i++) {
    14. Field fld = fieldlist[i];
    15. System.out.println("name
    16. = " + fld.getName());
    17. System.out.println("decl class = " +
    18. fld.getDeclaringClass());
    19. System.out.println("type
    20. = " + fld.getType());
    21. int mod = fld.getModifiers();
    22. System.out.println("modifiers = " + "
    23. Modifier.toString(mod));
    24. System.out.println("-----");
    25. }
    26. }
    27. catch (Throwable e) {
    28. System.err.println(e);
    29. }
    30. }
    31. }

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

    1. name = d
    2. decl class = class field1
    3. type = double
    4. modifiers = private
    5. -----
    6. name = i
    7. decl class = class field1
    8. type = int
    9. modifiers = public static final
    10. -----
    11. name = s
    12. decl class = class field1
    13. type = class java.lang.String
    14. modifiers =
    15. -----

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

    按名称调用方法

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

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

    1. import java.lang.reflect.*;
    2. public class method2 {
    3. public int add(int a, int b)
    4. {
    5. return a + b;
    6. }
    7. public static void main(String args[])
    8. {
    9. try {
    10. Class cls = Class.forName("method2");
    11. Class partypes[] = new Class[2];
    12. partypes[0] = Integer.TYPE;
    13. partypes[1] = Integer.TYPE;
    14. Method meth = cls.getMethod(
    15. "add", partypes);
    16. method2 methobj = new method2();
    17. Object arglist[] = new Object[2];
    18. arglist[0] = new Integer(37);
    19. arglist[1] = new Integer(47);
    20. Object retobj
    21. = meth.invoke(methobj, arglist);
    22. Integer retval = (Integer)retobj;
    23. System.out.println(retval.intValue());
    24. }
    25. catch (Throwable e) {
    26. System.err.println(e);
    27. }
    28. }
    29. }

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

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

    创建新对象

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

    1. import java.lang.reflect.*;
    2. public class constructor2 {
    3. public constructor2()
    4. {
    5. }
    6. public constructor2(int a, int b)
    7. {
    8. System.out.println(
    9. "a = " + a + " b = " + b);
    10. }
    11. public static void main(String args[])
    12. {
    13. try {
    14. Class cls = Class.forName("constructor2");
    15. Class partypes[] = new Class[2];
    16. partypes[0] = Integer.TYPE;
    17. partypes[1] = Integer.TYPE;
    18. Constructor ct
    19. = cls.getConstructor(partypes);
    20. Object arglist[] = new Object[2];
    21. arglist[0] = new Integer(37);
    22. arglist[1] = new Integer(47);
    23. Object retobj = ct.newInstance(arglist);
    24. }
    25. catch (Throwable e) {
    26. System.err.println(e);
    27. }
    28. }
    29. }

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

    更改字段的值

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

    1. import java.lang.reflect.*;
    2. public class field2 {
    3. public double d;
    4. public static void main(String args[])
    5. {
    6. try {
    7. Class cls = Class.forName("field2");
    8. Field fld = cls.getField("d");
    9. field2 f2obj = new field2();
    10. System.out.println("d = " + f2obj.d);
    11. fld.setDouble(f2obj, 12.34);
    12. System.out.println("d = " + f2obj.d);
    13. }
    14. catch (Throwable e) {
    15. System.err.println(e);
    16. }
    17. }
    18. }

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

    使用数组

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

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

    1. import java.lang.reflect.*;
    2. public class array1 {
    3. public static void main(String args[])
    4. {
    5. try {
    6. Class cls = Class.forName(
    7. "java.lang.String");
    8. Object arr = Array.newInstance(cls, 10);
    9. Array.set(arr, 5, "this is a test");
    10. String s = (String)Array.get(arr, 5);
    11. System.out.println(s);
    12. }
    13. catch (Throwable e) {
    14. System.err.println(e);
    15. }
    16. }
    17. }

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

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

    1. import java.lang.reflect.*;
    2. public class array2 {
    3. public static void main(String args[])
    4. {
    5. int dims[] = new int[]{5, 10, 15};
    6. Object arr
    7. = Array.newInstance(Integer.TYPE, dims);
    8. Object arrobj = Array.get(arr, 3);
    9. Class cls =
    10. arrobj.getClass().getComponentType();
    11. System.out.println(cls);
    12. arrobj = Array.get(arrobj, 5);
    13. Array.setInt(arrobj, 10, 37);
    14. int arrcast[][][] = (int[][][])arr;
    15. System.out.println(arrcast[3][5][10]);
    16. }
    17. }

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

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

    总结

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

     

  • 相关阅读:
    常用的深度学习自动标注软件
    Python爬虫——Requests 的Get和Post请求
    叶绿素含量测定仪SPAD-502怎么使用?
    计算机毕业设计选什么题目好?springboot 高校就业管理系统
    docker方式安装redis-自定义redis配置文件
    Linux操作系统概述3——进程相关操作讲解(进程概念、xinetd守护进程、进程管理命令)
    分布式id生成方案有哪些
    .NET混合开发解决方案7 WinForm程序中通过NuGet管理器引用集成WebView2控件
    产品-Axure9英文版,A页面内a1状态跳转B页面的b2状态,(条件跳转状态)
    数据结构——新手村级二叉树讲解
  • 原文地址:https://blog.csdn.net/m0_57042151/article/details/127978679