java

位置:IT落伍者 >> java >> 浏览文章

Java中利用Reflection API优化代码


发布日期:2022年11月17日
 
Java中利用Reflection API优化代码

摘要

开发者通过各种各样的方法来尝试避免单调冗余的编程一些编程的规则例如继承多态或者设计模型可以帮助开发者避免产生多余的代码不过由于软件开发方面存在着不确定性因此这些规则并不能消除代码维护和重新编写的需要在很多时候维护都是不可避免的只有不能运作的软件才是从不需要维护的不过这篇文章介绍了你可以使用Java的Reflection API的功能来减少单调的代码编写并可以使用活动的代码产生来克服reflection的限制

数据配置(由外部的源头得到数据并且将它装载到一个Java对象中)可以利用reflection的好处来创建一个可重用的方案问题是很简单的将数据由一个文件装入到一个对象的字段中现在假设用作数据的目标Java类每星期改变一次?有一个很直接的解决方法不过你必须不断地维护载入的过程来反映任何的改变在更复杂的环境下同样的问题可能会令系统崩溃掉对于一个处理过运用XML的大型系统的人来说他就会遇到过这个问题要编写一个载入的过程通常是非常单调乏味的由于数据源或者目标Java类的改变你需要经常更新和重新编写代码在这里我要介绍另一个解决方案那就是使用映射它通常使用更少的编码并且可以在目标Java类发生改变后更新自己

最初我想介绍一个使用Reflection在运行期间配置数据的方案在开始的时候一个动态基于映射的程序要比一个简单的方法更有吸引力多了随后我要揭示出运行时Reflection的复杂性和冒险性这篇文章将介绍由运行时的Reflection到活动的代码产生

由简单到复杂

我的第一个方案使用一个载入类将数据从一个文件载入到对象中我的源代码含有对StringTokenizer对象下一节点方法的多次调用在修改多次后我的编码逻辑变得非常的直接系统化该类构造了专用的代码在这个初始方案中我只需要使用个基本的对象

Strings

Objects

Arrays of objects

你可以影射类的对象来产生代码块如下表所示

被影射来产生代码块的对象

我已经使用这个方案作了几次编码因此我在写代码之前我已经知道该方案和代码的结构难点在于该类是变化的类的名字成份和结构在任何时候都可能发生变化而任何的改变你都要重新编写代码虽然会发生这些变化但是结构和下载的流程仍然是一样的在写代码前我仍然知道代码的结构和成份我需要一个方法来将头脑中的编码流程转换为一个可重用的自动的形式由于我是一个有效率的编程者我很快就厌倦了编写几乎一样的代码这时我想到了映射

数据配置通常需要一个源到目的数据的影射影射可以是一个图解DTD(document type definition文档类型定义)文件格式等在这个例子中映射将一个对象的类定义解释为我们要映射的流程映射可以在运行时复制代码的功能在需要重写代码时我将载入的过程用映射来代替它所需要的时间和重写是一样的

载入的工程可以概括为以下几步

解释一个影射决定你在构造一个对象时需要些什么

请求数据要满足构造的需要要进行一个调用来得到数据

数据由源中得到

数据被填充入一个对象的新实例

如果必要的话重复步骤

你需要以下的类来满足以上的步骤

.数据类(Data classes)由ASCII文件中的数据实例化类定义提供数据的影射数据类必须满足以下的条件

它们必须包含有一个构造器来接收全部必需的参数以使用一个有效的状态来构造对象

它们必须由对象构成这些对象是reflective过程知道如何处理的

.对象装载器(Object loader)使用reflection和数据类作为一个影射来载入数据产生数据请求

.载入管理器(Load manager)作为对象装载器和数据源的中介层将对数据的请求转换为一个数据指定的调用这可以令对象载入器做到与数据源无关通过它的接口和一个可载入的类对象通信

.数据循环接口(Data iterator interface)载入管理器和载入类对象使用这个接口来由数据源中得到数据

一旦你创建了支持的类你就可以使用以下的声明来创建和影射一个对象

FooFileIterator iter = new FooFileIterator(fileLocation log);

LoadManager manager = new FooFileLoadManager(iter);

SubFooObject obj =

(SubFooObject)ReflectiveObjectLoaderinitializeInstance(SubFooObjectclass managerlog);

通过这个处理你就创建了一个包含有文件内容的SubFooObject实例

局限

开发者必须决定使用哪个方案来解决问题是最好的通常做出这个决定是最困难的部分在考虑使用reflection作数据配置时你要考虑到以下一些限制

不要令一个简单的问题复杂化reflection是比较复杂的因此在必要的时候才使用它一旦开发者明白了reflection的能力他就想使用它来解决所有的问题如果你有更快更简单的方案来解决问题时你就不应该使用reflection(即使这个更好的方案可能使用更多的代码)reflection是强大的但也有一些风险

考虑性能reflection对性能的影响比较大因为要在运行时发现和管理类属性需要时间和内存

重新评估方案

如上所述使用运行时reflection的第一个限制是不要令简单的问题复杂化在使用reflection时这是不可避免的将reflection和递归结合起来是一个令人头痛的问题重新看代码也是一件可怕的事情而准确决定代码的功能也是非常复杂的要知道代码的准确作用的唯一方法是使用一些取样数据逐行地看就象运行时一样不过对于每个可能的数据组合都使用这种方式几乎是不可能的在这种情况下使用单元测试代码可能有些帮助不过也很可能出现错误幸运的是还有一个可选的方法

可选的方法

由上面列出的限制可以看到在某些情况下使用reflective载入过程可能是得不偿失的代码产生提供了一个通用的选择方法你也可以使用reflection来检查一个类并且为载入过程产生代码

Andrew Hunt和David Thomas介绍了两类的代码产生器见The Pragmatic Programmer(/jwl#resources)

Passive(被动)被动的代码产生器在实现代码时需要人工的干预许多的IDE(集成开发环境)都提供相应的向导来实现

Active(主动)主动的代码产生指的是代码一旦创建就不再需要修改了如果有问题产生这个问题也应该在代码产生器中解决而不是在产生的源文件中解决在理想的情况下这个过程应该包含在编译的处理过程中从而确保类不会过期

代码产生的优点和缺点包含有以下方面

优点

.简单产生的代码通常是更便于开发者阅读和调试

.编译过程的错误Reflexive在运行时出现错误的机会要比编译的期间多例如改变被载入的对象将有可能令产生的载入类抛出一个编译的错误不过reflexive过程将不会看到任何的区别直到在运行时遇到这个类

缺点

.维护使用被动的代码产生修改被载入的对象将需要更新或者重新产生载入的类如果该类被重新产生那么自定义的东西就会丢失

回头再来看看主动代码产生的好处

在这里我们可以看到在运行时使用reflection是不可以接受的主动的代码产生有着reflection的全部好处但是没有它的限制还可以继续使用reflection不过只是在代码的产生过程而不是运行的过程理由如下

更少冒险运行时的reflection明显是更冒险的特别是问题变得复杂的时候

基于单元测试但并不是编译器

多功能性产生的代码有着runtime reflection的全部好处而且有着runtime reflection没有的好处

更易懂虽然经过多次的处理但是将递归和reflection结合仍然是很复杂的产生源代码的方式更加容易解释和理解代码产生过程需要递归和reflection但得到的结果是可查看的源代码而不是难以理解的东西

写代码产生器

要写一个代码产生器在思考的时候你不能只是简单地编写一个方案来解决一个问题你应该看得更远代码产生器(以及reflection)需要你作更多的思考如果你只是使用runtime reflection你就不得不在运行时概念化问题而不是使用简单兼容性好的源代码来解决问题代码产生要求你从两个方面来查看问题代码产生过程会将抽象的概念转变为实际的源代码Runtime reflection则一直是抽象的

代码产生过程将你的思考过程转变为代码然后产生并且编译代码编译器会让你知道你的思考过程在语法上是否正确单元测试则可以验证代码在运行时的行为就动态特性方面runtime reflection就不能达到这个级别的安全性

代码产生器

在经历后几次失败的设计后我认为最简单的方法是在载入过程中为每一种需要实例化的类产生一个方法一个方法工厂产生每个特别类的正确方法一个代码编译对象缓沖来自代码工厂的方法请求以产生最终源代码文件的内容

MethodCode对象是代码产生过程的核心以下就是一个int的代码产生对象的例子

public class MethodForInt extends MethodCode {

private final static MethodParameter param = new MethodParameter(SimpleFileIteratorclass parser);

public MethodForInt(Class type CodeBuilder builder){

super(type builder);

}

public MethodParameter[] getInputParameters(){

return new MethodParameter[]{

param

};

}               

上一篇:JAVA开发环境搭建 win7 64位

下一篇:逐步深入剖析java类的构造方式