黑马程序员技术交流社区

标题: 基于类的反射 [打印本页]

作者: Jam-l    时间: 2013-9-27 23:57
标题: 基于类的反射
本帖最后由 Jam-l 于 2013-9-27 23:59 编辑

对于以下三类组件中的任何一类来说 -- 构造函数、字段和方法 -- java.lang.Class 提供四种独立的反射调用,以不同的方式来获得信息。调用都遵循一种标准格式。以下是用于查找构造函数的一组反射调用:
每类这些调用都返回一个或多个 java.lang.reflect.Constructor 函数。这种 Constructor 类定义 newInstance 方法,它采用一组对象作为其唯一的参数,然后返回新创建的原始类实例。该组对象是用于构造函数调用的参数值。作为解释这一工作流程的实例,假设您有一个 TwoString 类和一个使用一对 String s的构造函数,如清单1所示:

清单1:从一对字符串创建的类
  1. public class TwoString {
  2.     private String m_s1, m_s2;
  3.     public TwoString(String s1, String s2) {
  4.         m_s1 = s1;
  5.         m_s2 = s2;
  6.     }
  7. }
复制代码
清单2中的代码获得构造函数并使用它来创建使用 String s "a" 和 "b" 的 TwoString 类的一个实例:

清单2:构造函数的反射调用


  1. Class[] types = new Class[] { String.class, String.class };
  2.     Constructor cons = TwoString.class.getConstructor(types);
  3.     Object[] args = new Object[] { "a", "b" };
  4.     TwoString ts = cons.newInstance(args);
复制代码
清单2中的代码忽略了不同反射方法抛出的多种可能选中的例外类型。例外在 Javadoc API 描述中详细记录,因此为了简明起见,我将在所有程序实例中忽略它们。
尽管我在讨论构造函数主题,Java编程语言还定义了一种您可以用来使用 无参数(或缺省)构造函数创建类的一个实例的特殊快捷方式。这种快捷方式嵌入到 Class 定义中,如下:
Object newInstance() -- 使用缺省函数创建新的实例
即使这种方法只允许您使用一种特殊的构造函数,如果这正是您需要的,那么它将提供一种非常方便的快捷方式。当与JavaBeans协作时这项技术尤其有用,JavaBeans需要定义公共、无参数构造函数。
通过反射增加字段
获得字段信息的 Class 反射调用不同于那些用于接入构造函数的调用,在参数类型数组中使用了字段名:
尽管与构造函数调用类似,在字段方面仍存在一个重要的区别:前两个变量返回可以通过类接入的公共字段的信息 -- 即使它们来自于祖先类。后两个变量返回类直接声明的字段的信息 -- 与字段的接入类型无关。
调用返回的 java.lang.reflect.Field 实例定义所有主类型的 getXXX 和 setXXX 方法,以及与对象引用协作的通用 get 和 set 方法。您可以根据实际的字段类型自行选择一种适当的方法,而 getXXX 方法将自动处理扩展转换(如使用 getInt 方法来检索一个字节值)。
清单3显示使用字段反射方法的一个实例,以方法的格式根据名称增加对象的 int 字段 :

清单3:通过反射增加一个字段

  1. public int incrementField(String name, Object obj) throws... {
  2.     Field field = obj.getClass().getDeclaredField(name);
  3.     int value = field.getInt(obj) + 1;
  4.     field.setInt(obj, value);
  5.     return value;
  6. }
复制代码
这种方法开始展示了反射带来的某些灵活性。与特定的类协作不同, incrementField 使用传 入的对象的 getClass 方法来查找类信息,然后直接在该类中查找命名的字段。
通过反射增加方法
获得方法信息的 Class 反射调用与用于构造函数和字段的调用非常类似:
  • Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法
  • Method[] getMethods() -- 获得类的所有公共方法
  • Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法
  • Method[] getDeclaredMethods() -- 获得类声明的所有方法
与字段调用一样,前两个变量返回可以通过类接入的公共方法的信息 -- 即使它们来自于祖先类。后两个变量返回类声明的方法的信息,与方法的接入类型无关。
调用返回的 java.lang.reflect.Method 实例定义一种 invoke 方法,您可以用来在正在定义的类的一个实例上调用方法。这种 invoke方法使用两个参数,为调用提供类实例和参数值数组。
清单4进一步阐述字段实例,显示反射正在运行的方法的一个实例。这种方法增加一个定义有 get 和 set 方法的 int JavaBean属性。例如,如果对象为一个整数 count 值定义了 getCount 和 setCount 方法,您可以在一次调用中向该方法传递“count”作为 name 参数,以增加该值。

清单4:通过反射增加一个JavaBean 属性
  1. public int incrementProperty(String name, Object obj) {
  2.     String prop = Character.toUpperCase(name.charAt(0)) +
  3.         name.substring(1);
  4.     String mname = "get" + prop;
  5.     Class[] types = new Class[] {};
  6.     Method method = obj.getClass().getMethod(mname, types);
  7.     Object result = method.invoke(obj, new Object[0]);
  8.     int value = ((Integer)result).intValue() + 1;
  9.     mname = "set" + prop;
  10.     types = new Class[] { int.class };
  11.     method = obj.getClass().getMethod(mname, types);
  12.     method.invoke(obj, new Object[] { new Integer(value) });
  13.     return value;
  14. }
复制代码
为了遵循JavaBeans惯例,我把属性名的首字母改为大写,然后预先考虑 get 来创建读方法名, set 来创建写方法名。JavaBeans读方法仅返回值,而写方法使用值作为唯一的参数,因此我规定方法的参数类型以进行匹配。最后,该惯例要求方法为公共,因此我使用查找格式,查找类上可调用的公共方法。
这一实例是第一个我使用反射传递主值的实例,因此现在我们来看看它是如何工作的。基本原理很简单:无论什么时候您需要传递主值,只需用相应封装类的一个实例(在 java.lang 包中定义)来替换该类主值。这可以应用于调用和返回。因此,当我在实例中调用get 方法时,我预计结果为实际 int 属性值的 java.lang.Integer 封装。





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2