友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

Java编程思想第4版[中文版](PDF格式)-第115章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




      // Requires default constructor:  

      o = cy。newInstance(); // (*1*)  

    } catch(InstantiationException e) {}  

      catch(IllegalAccessException e) {}  



                                                                                             342 


…………………………………………………………Page 344……………………………………………………………

    printInfo(o。getClass());  

  }  

  static void printInfo(Class cc) {  

    System。out。println(  

      〃Class name: 〃 + cc。getName() +  

      〃  is interface? '〃 +  

      cc。isInterface() + 〃'〃);  

  }  

} ///:~  

  

从中可以看出,class FancyToy 相当复杂,因为它从Toy 中继承,并实现了HasBatteries,Waterproof 以 

及ShootsThings 的接口。在main()中创建了一个Class 句柄,并用位于相应 try 块内的 forName()初始化成 

FancyToy 。  

Class。getInterfaces 方法会返回 Class 对象的一个数组,用于表示包含在 Class 对象内的接口。  

若有一个Class 对象,也可以用getSuperclass()查询该对象的直接基础类是什么。当然,这种做会返回一 

个Class 句柄,可用它作进一步的查询。这意味着在运行期的时候,完全有机会调查到对象的完整层次结 

构。  

若从表面看,Class 的newInstance()方法似乎是克隆(clone())一个对象的另一种手段。但两者是有区别 

的。利用newInstance() ,我们可在没有现成对象供“克隆”的情况下新建一个对象。就象上面的程序演示 

的那样,当时没有Toy 对象,只有 cy——即y 的Class 对象的一个句柄。利用它可以实现“虚拟构建器”。 

换言之,我们表达:“尽管我不知道你的准确类型是什么,但请你无论如何都正确地创建自己。”在上述例 

子中,cy 只是一个Class 句柄,编译期间并不知道进一步的类型信息。一旦新建了一个实例后,可以得到 

Object 句柄。但那个句柄指向一个Toy 对象。当然,如果要将除Object 能够接收的其他任何消息发出去, 

首先必须进行一些调查研究,再进行造型。除此以外,用 newInstance()创建的类必须有一个默认构建器。 

没有办法用 newInstance()创建拥有非默认构建器的对象,所以在Java 1。0 中可能存在一些限制。然而, 

Java 1。1 的“反射”API (下一节讨论)却允许我们动态地使用类里的任何构建器。  

程序中的最后一个方法是printInfo(),它取得一个Class 句柄,通过 getName()获得它的名字,并用 

interface()调查它是不是一个接口。  

该程序的输出如下:  

  

Class name: FancyToy is interface? 'false'  

Class name: HasBatteries is interface? 'true'  

Class name: Waterproof is interface? 'true'  

Class name: ShootsThings is interface? 'true'  

Class name: Toy is interface? 'false'  

  

所以利用Class 对象,我们几乎能将一个对象的祖宗十八代都调查出来。  



11。3 反射:运行期类信息  



如果不知道一个对象的准确类型,RTTI 会帮助我们调查。但却有一个限制:类型必须是在编译期间已知的, 

否则就不能用RTTI 调查它,进而无法展开下一步的工作。换言之,编译器必须明确知道 RTTI 要处理的所有 

类。  

从表面看,这似乎并不是一个很大的限制,但假若得到的是一个不在自己程序空间内的对象的句柄,这时又 

会怎样呢?事实上,对象的类即使在编译期间也不可由我们的程序使用。例如,假设我们从磁盘或者网络获 

得一系列字节,而且被告知那些字节代表一个类。由于编译器在编译代码时并不知道那个类的情况,所以怎 

样才能顺利地使用这个类呢?  

在传统的程序设计环境中,出现这种情况的概率或许很小。但当我们转移到一个规模更大的编程世界中,却 

必须对这个问题加以高度重视。第一个要注意的是基于组件的程序设计。在这种环境下,我们用“快速应用 

开发”(RAD)模型来构建程序项目。RAD 一般是在应用程序构建工具中内建的。这是编制程序的一种可视途 

径(在屏幕上以窗体的形式出现)。可将代表不同组件的图标拖曳到窗体中。随后,通过设定这些组件的属 

性或者值,进行正确的配置。设计期间的配置要求任何组件都是可以“例示”的(即可以自由获得它们的实 

例)。这些组件也要揭示出自己的一部分内容,允许程序员读取和设置各种值。此外,用于控制 GUI 事件的 



                                                                              343 


…………………………………………………………Page 345……………………………………………………………

组件必须揭示出与相应的方法有关的信息,以便RAD 环境帮助程序员用自己的代码覆盖这些由事件驱动的方 

法。“反射”提供了一种特殊的机制,可以侦测可用的方法,并产生方法名。通过 Java Beans (第13章将 

详细介绍),Java 1。1 为这种基于组件的程序设计提供了一个基础结构。  

在运行期查询类信息的另一个原动力是通过网络创建与执行位于远程系统上的对象。这就叫作“远程方法调 

用”(RMI),它允许Java 程序(版本 1。1 以上)使用由多台机器发布或分布的对象。这种对象的分布可能 

是由多方面的原因引起的:可能要做一件计算密集型的工作,想对它进行分割,让处于空闲状态的其他机器 

分担部分工作,从而加快处理进度。某些情况下,可能需要将用于控制特定类型任务(比如多层客户/服务 

器架构中的“运作规则”)的代码放置在一台特殊的机器上,使这台机器成为对那些行动进行描述的一个通 

用储藏所。而且可以方便地修改这个场所,使其对系统内的所有方面产生影响(这是一种特别有用的设计思 

路,因为机器是独立存在的,所以能轻易修改软件!)。分布式计算也能更充分地发挥某些专用硬件的作 

用,它们特别擅长执行一些特定的任务——例如矩阵逆转——但对常规编程来说却显得太夸张或者太昂贵 

了。  

在Java 1。1 中,Class 类(本章前面已有详细论述)得到了扩展,可以支持“反射”的概念。针对 Field, 

Method 以及Constructor 类(每个都实现了 Memberinterface——成员接口),它们都新增了一个库: 

java。lang。reflect。这些类型的对象都是 JVM 在运行期创建的,用于代表未知类里对应的成员。这样便可用 

构建器创建新对象,用 get()和 set()方法读取和修改与 Field 对象关联的字段,以及用 invoke()方法调用 

与Method 对象关联的方法。此外,我们可调用方法 getFields(),getMethods(),getConstructors(),分 

别返回用于表示字段、方法以及构建器的对象数组(在联机文档中,还可找到与Class 类有关的更多的资 

料)。因此,匿名对象的类信息可在运行期被完整的揭露出来,而在编译期间不需要知道任何东西。  

大家要认识的很重要的一点是“反射”并没有什么神奇的地方。通过“反射”同一个未知类型的对象打交道 

时,JVM 只是简单地检查那个对象,并调查它从属于哪个特定的类(就象以前的RTTI 那样)。但在这之后, 

在我们做其他任何事情之前,Class 对象必须载入。因此,用于那种特定类型的。class文件必须能由 JVM 调 

用(要么在本地机器内,要么可以通过网络取得)。所以 RTTI 和“反射”之间唯一的区别就是对 RTTI 来 

说,编译器会在编译期打开和检查。class文件。换句话说,我们可以用“普通”方式调用一个对象的所有方 

法;但对“反射”来说,。class 文件在编译期间是不可使用的,而是由运行期环境打开和检查。  



11。3。1 一个类方法提取器  



很少需要直接使用反射工具;之所以在语言中提供它们,仅仅是为了支持其他Java 特性,比如对象序列化 

 (第10章介绍)、Java Beans 以及RMI (本章后面介绍)。但是,我们许多时候仍然需要动态提取与一个类 

有关的资料。其中特别有用的工具便是一个类方法提取器。正如前面指出的那样,若检视类定义源码或者联 

机文档,只能看到在那个类定义中被定义或覆盖的方法,基础类那里还有大量资料拿不到。幸运的是,“反 

射”做到了这一点,可用它写一个简单的工具,令其自动展示整个接口。下面便是具体的程序:  

  

//: ShowMethods。java  

// Using Java 1。1 reflection to show all the   

// methods of a class; even if the methods are   

// defined in the base class。  

import java。lang。reflect。*;  

  

public class ShowMethods {  

  static final String usage =  

    〃usage: n〃 +  

    〃ShowMethods qualified。class。namen〃 +  

    〃To show all methods in class or: n〃 +  

    〃ShowMethods qualified。class。name wordn〃 +  

    〃To search for methods involving 'word'〃;  

  public static void main(String'' args) {  

    if(args。length 《 1) {  

      System。out。println(usage);  

      System。exit(0);  

    }  

    try {  



                                                                              344 


…………………………………………………………Page 346……………………………………………………………

      Class c = Class。forName(args'0');  

      Method'' m = c。getMethods();  

      Constructor'' ctor = c。getConstructors();  

      if(args。length == 1) {  

        for (int i = 0; i 《 m。length; i++)  

          System。out。println(m'i'。toString());  

        for (int i = 0; i 《 ctor。length; i++)  

          System。out。println(ctor'i'。toString());  

      }   

      else {  

        for (int i = 0; i 《 m。length; i++)  

          if(m'i'。toString()  

             。indexOf(args'1')!= …1)  

            System。out。println(m'i'。toString());  

        for (int i = 0; i 《 ctor。length; i++)  

          if(ctor'i'。toString()  

             。indexOf(args'1')!= …1)  

          System。out。println(ctor'i'。toString());  

      }  

    } catch (ClassNotFoundExcep
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!