资源预览内容
第1页 / 共9页
第2页 / 共9页
第3页 / 共9页
第4页 / 共9页
第5页 / 共9页
第6页 / 共9页
第7页 / 共9页
第8页 / 共9页
第9页 / 共9页
亲,该文档总共9页全部预览完了,如果喜欢就下载吧!
资源描述
Java编程 的动态性,第 2部分: 引入反射使用运行时类信息来准备编程Dennis M. Sosnoski (dmssosnoski.com), 总裁, Sosnoski Software Solutions, Inc.2003 年 8 月 23 日反射使您的程序代码能够接入装载到JVM中的类的内部信息,允许您编写与执行时,而不是源代码中选定的类协作的代码。这使反射成为构建灵活的应用的主要工具。但需注意的是 -如果使用不当,反射的成本很高。在Java平台系列的第2部分中,软件顾问Dennis Sosnoski介绍了如何使用反射,以及某些相关的成本。您还将找到JavaReflection API如何使您能够在运行时关联对象。在“ Java编程的动态性,第1部分,”我为您介绍了Java编程类和类装入。该篇文章介绍了一些Java二进制类格式的相关信息。这个月我将阐述使用Java反射API来在运行时接入和使用一些相同信息的基础。为了使已经熟知反射基础的开发人员关注本文,我将在文章中包括反射性能如何与直接接入相比较。 使用反射不同于常规的Java编程,其中它与 元数据-描述其它数据的数据协作。Java语言反射接入的特殊类型的原数据是JVM中类和对象的描述。反射使您能够运行时接入广泛的类信息。它甚至使您能够读写字段,调用运行时选择的类的方法。 反射是一种强大的工具。它使您能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代表链接。但反射的某些方面存在一些疑问。在本文中,我将深入讨论为什么您可能 不希望在程序中使用反射,以及您应该这样做的理由。在了解了权衡性分析之后,您可以自行决定是否利大于弊。 初学者的类使用反射的启点总是 java.lang.Class 实例。如果您希望与预先定义的类协作,那么Java语言提供一种直接获得 Class 实例的简便快捷方式: 不要错过本系列的其它文章第1部分,“ 类和类装入” (2003年4月) Class clas = MyClass.class;当您使用这一项技术时,装入类涉及的所有工作在幕后进行。但是,如果您需要在运行时从某些外部源读取类名,这种方法并不适合。实际上,您需要使用一个类装入器来查找类信息。以下介绍一种方法:/ name is the class name to loadClass clas = null;try clas = Class.forName(name); catch (ClassNotFoundException ex) / handle exception case/ use the loaded class如果已经装入了类,您将得到现有的 Class 信息。如果类未被装入,类装入器将现在装入并返回新创建的类实例。 回页首基于类的反射Class 对象为您提供接入类元数据的反射的所有基本hook。这类元数据包括关于类自身的信息,如包和类的父类,以及该类实施的接口。它还包括该类定义的构造函数、字段和方法的详细信息。这些最后的项目都是编程中最经常使用的项目,因此我将在本小节的稍后部分给出一些与它们协作的实例。 对于以下三类组件中的任何一类来说 - 构造函数、字段和方法 - java.lang.Class 提供四种独立的反射调用,以不同的方式来获得信息。调用都遵循一种标准格式。以下是用于查找构造函数的一组反射调用: Constructor getConstructor(Class params) - 获得使用特殊的参数类型的公共构造函数, Constructor getConstructors() - 获得类的所有公共构造函数 Constructor getDeclaredConstructor(Class params) - 获得使用特定参数类型的构造函数(与接入级别无关) Constructor getDeclaredConstructors() - 获得类的所有构造函数(与接入级别无关) 每类这些调用都返回一个或多个 java.lang.reflect.Constructor 函数。这种 Constructor 类定义 newInstance 方法,它采用一组对象作为其唯一的参数,然后返回新创建的原始类实例。该组对象是用于构造函数调用的参数值。作为解释这一工作流程的实例,假设您有一个 TwoString 类和一个使用一对 String s的构造函数,如清单1所示: 清单1:从一对字符串创建的类 public TwoString(String s1, String s2) m_s1 = s1; m_s2 = s2; 清单2中的代码获得构造函数并使用它来创建使用 String s a 和 b 的 TwoString 类的一个实例: 清单2:构造函数的反射调用 Class types = new Class String.class, String.class ; Constructor cons = TwoString.class.getConstructor(types); Object args = new Object a, b ; TwoString ts = cons.newInstance(args);清单2中的代码忽略了不同反射方法抛出的多种可能选中的例外类型。例外在 Javadoc API 描述中详细记录,因此为了简明起见,我将在所有程序实例中忽略它们。尽管我在讨论构造函数主题,Java编程语言还定义了一种您可以用来使用 无参数(或缺省)构造函数创建类的一个实例的特殊快捷方式。这种快捷方式嵌入到 Class 定义中,如下: Object newInstance() - 使用缺省函数创建新的实例 即使这种方法只允许您使用一种特殊的构造函数,如果这正是您需要的,那么它将提供一种非常方便的快捷方式。当与JavaBeans协作时这项技术尤其有用,JavaBeans需要定义公共、无参数构造函数。通过反射增加字段获得字段信息的 Class 反射调用不同于那些用于接入构造函数的调用,在参数类型数组中使用了字段名: Field getField(String name) - 获得命名的公共字段 Field getFields() - 获得类的所有公共字段 Field getDeclaredField(String name) - 获得类声明的命名的字段 Field getDeclaredFields() - 获得类声明的所有字段 尽管与构造函数调用类似,在字段方面仍存在一个重要的区别:前两个变量返回可以通过类接入的公共字段的信息 - 即使它们来自于祖先类。后两个变量返回类直接声明的字段的信息 - 与字段的接入类型无关。调用返回的 java.lang.reflect.Field 实例定义所有主类型的 getXXX 和 setXXX 方法,以及与对象引用协作的通用 get 和 set 方法。您可以根据实际的字段类型自行选择一种适当的方法,而 getXXX 方法将自动处理扩展转换(如使用 getInt 方法来检索一个字节值)。 清单3显示使用字段反射方法的一个实例,以方法的格式根据名称增加对象的 int 字段 : 清单3:通过反射增加一个字段public int incrementField(String name, Object obj) throws. Field field = obj.getClass().getDeclaredField(name); int value = field.getInt(obj) + 1; field.setInt(obj, value); return value;这种方法开始展示了反射带来的某些灵活性。与特定的类协作不同, 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 属性public int incrementProperty(String name, Object obj) String prop = Character.toUpperCase(name.charAt(0) + name.substring(1); String mname = get + prop; Class types = new Class ; Method method = obj.getClass().getMethod(mname, types); Object result = method.invoke(obj, new Object0); int value = (Integer)result).intValue() + 1; mname = set + prop; types = new Class int.class ; method = obj.getClass().getMethod(mname, types); method.invoke(obj, new Object new Integer(value) ); return value;为了遵循JavaBeans惯例,我把属性名的首字母改为大写,然后预先考虑 get 来创建读方法名, set 来创建写方法名。JavaBeans读方法仅返回值,而写方法使
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号