友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
富士康小说网 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

Java编程思想第4版[中文版](PDF格式)-第113部分

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!



令里不规定想调用哪个版本,JVM 就会试着自行判断。但这一操作会在程序执行时花费较长的时间。所以, 

我们一般可用ansi,unicode 或 auto 修改符硬性规定。  

欲了解这些特性更详细的情况,请参考微软公司提供的技术文档。  



A。4 本原接口(RNI )  



同J/Direct 相比,RNI 是一种比非 Java 代码复杂得多的接口;但它的功能也十分强大。RNI 比J/Direct 更 

接近于 JVM,这也使我们能写出更有效的代码,能处理固有方法中的 Java 对象,而且能实现与JVM 内部运行 

机制更紧密的集成。  

RNI 在概念上类似Sun 公司的JNI。考虑到这个原因,而且由于该产品尚未正式完工,所以我只在这里指出它 

们之间的主要差异。欲了解更详细的情况,请参考微软公司的文档。  

JNI 和 RNI 之间存在几方面引人注目的差异。下面列出的是由msjavah 生成的 C 头文件(微软提供的msjavah 

在功能上相当于Sun 的 javah),应用于前面在JNI 例子里使用的 Java 类文件 ShowMsgBox。  

  

/*  DO NOT EDIT   

automatically generated by msjavah  */  

#include   

#pragma warning(disable:4510)  

#pragma warning(disable:4512)  

#pragma warning(disable:4610)  

  

struct Classjava_lang_String;  

#define Hjava_lang_String Classjava_lang_String  

  

/*  Header for class ShowMsgBox  */  

  

#ifndef _Included_ShowMsgBox  

#define _Included_ShowMsgBox  

  

#define HShowMsgBox ClassShowMsgBox  

typedef struct ClassShowMsgBox {  

#include   

  long MSReserved;  

#include   

} ClassShowMsgBox;  

  

#ifdef __cplusplus  

extern 〃C〃 {  

#endif  

__declspec(dllexport) void __cdecl   

ShowMsgBox_ShowMessage (struct HShowMsgBox *;   

  struct Hjava_lang_String *);  

#ifdef __cplusplus  

}  

#endif  

  

#endif  /* _Included_ShowMsgBox */  

  

#pragma warning(default:4510)  



                                                                                             660 


…………………………………………………………Page 662……………………………………………………………

#pragma warning(default:4512)  

#pragma warning(default:4610)  

  

除可读性较差外,代码里还隐藏着一些技术性问题,待我一一道来。  

在RNI 中,固有方法的程序员知道对象的二进制布局。这样便允许我们直接访问自己希望的信息;我们不必 

象在JNI 里那样获得一个字段或方法标识符。但由于并非所有虚拟机都需要将相同的二进制布局应用于自己 

的对象,所以上面的固有方法只能在Microsoft JVM 下运行。  

在JNI 中,通过JNIEnv 自变量可访问大量函数,以便同JVM 打交道。在 RNI 中,用于控制JVM 运作的函数变 

成了可直接调用。它们中的某一些(如控制异常的那一个)类似于它们的 JNI “兄弟”。但大多数RNI 函数 

都有与JNI 中不同的名字和用途。  

JNI 和 RNI 最重大的一个区别是“垃圾收集”的模型。在 JNI 中,垃圾收集在固有方法执行期间遵守与Java 

代码执行时相同的规则。而在 RNI 中,要由程序员在固有方法活动期间自行负责“垃圾收集器”器的启动与 

中止。默认情况下,垃圾收集器在进入固有方法前处于不活动状态;这样一来,程序员就可假定准备使用的 

对象用不着在那个时间段内进行垃圾收集。然而一旦固有方法准备长时间执行,程序员就应考虑激活垃圾收 

集器——通过调用GCEnable()这个RNI 函数(GC 是“Garbage Collector”的缩写,即“垃圾收集”)。  

也存在与全局句柄特性类似的机制——程序员可利用可保证特定的对象在 GC 活动期间不至于被当作“垃圾” 

收掉。概念是类似的,但名称有所差异——在 RNI 中,人们把它叫作GCFrames 。  



A。4。1 RNI 总结  



RNI 与 Microsoft JVM 紧密集成这一事实既是它的优点,也是它的缺点。RNI 比JNI 复杂得多,但它也为我们 

提供了对JVM 内部活动的高度控制;其中包括垃圾收集。此外,它显然针对速度进行了优化,采纳了C 程序 

员熟悉的一些折衷方案和技术。但除了微软的 JVM 之外,它并不适于其他 JVM。  



A。5 Java/ 集成  



 (以前称为OLE)代表微软公司的“组件对象模型”(ponent Object Model ),它是所有ActiveX 技 

术(包括ActiveX 控件、Automation 以及ActiveX 文档)的基础。但  还包含了更多的东西。它是一种特 

殊的规范,按照它开发出来的组件对象可通过操作系统的专门特性实现“相互操作”。在实际应用中,为 

Win32 系统开发的所有新软件都与 有着一定的关系——操作系统通过  对象揭示出自己的一些特性。 

由其他厂商开发的组件也可以建立在 的基础上,我们能创建和注册自己的 组件。通过这样或那样的 

形式,如果我们想编写 Win32 代码,那么必须和  打交道。在这里,我们将仅仅重述  编程的基本概 

念,而且假定读者已掌握了 服务器(能为  客户提供服务的任何 对象)以及  客户(能从 

服务器那里申请服务的一个 对象)的概念。本节将尽可能地使叙述变得简单。工具实际的功能要强大得 

多,而且我们可通过更高级的途径来使用它们。但这也要求对  有着更深刻的认识,那已经超出了本附录 

的范围。如果您对这个功能强大、但与不同平台有关的特性感兴趣,应该研究 和微软公司的文档资料, 

仔细阅读有关Java/ 集成的那部分内容。如果想获得更多的资料,向您推荐 Dale Rogerson 编著的 

 《Inside 》,该书由 Microsoft Press 于 1997 年出版。  

由于 是所有新型 Win32 应用程序的结构核心,所以通过Java 代码使用(或揭示) 服务的能力就显得 

尤为重要。Java/ 集成无疑是 Microsoft Java 编译器以及虚拟机最有趣的特性。Java 和 在它们的模 

型上是如此相似,所以这个集成在概念上是相当直观的,而且在技术上也能轻松实现无缝结合——为访问 

,几乎不需要编写任何特殊的代码。大多数技术细节都是由编译器和/或虚拟机控制的。最终的结果便是 

Java 程序员可象对待原始Java 对象那样对待  对象。而且  客户可象使用其他  服务器那样使用由 

Java 实现的 服务器。在这里提醒大家,尽管我使用的是通用术语“”,但根据扩展,完全可用Java 

实现一个ActiveX Automation 服务器,亦可在Java 程序中使用一个ActiveX 控件。  

Java 和 最引人注目的相似之处就是 接口与 Java 的“interface”关键字的关系。这是接近完美的一 

种相符,因为:  

■ 对象揭示出了接口(也只有接口)  

■ 接口本身并不具备实施方案;要由揭示出接口的那个 对象负责它的实施  

■ 接口是对语义上相关的一组函数的说明;不会揭示出任何数据  

■ 类将 接口组合到了一起。Java 类可实现任意数量的 Java 接口。  

■ 有一个引用对象模型;程序员永远不可能“拥有”一个对象,只能获得对对象一个或多个接口的引 

用。Java 也有一个引用对象模型——对一个对象的引用可“造型”成对它的某个接口的引用。  



                                                                    661 


…………………………………………………………Page 663……………………………………………………………

■ 对象在内存里的“生存时间”取决于使用对象的客户数量;若这个数量变成零,对象就会将自己从内 

存中删去。在Java 中,一个对象的生存时间也由客户的数量决定。若不再有对那个对象的引用,对象就会等 

候垃圾收集器的处理。  

  

Java 与 之间这种紧密的对应关系不仅使 Java 程序员可以方便地访问  特性,也使 Java 成为编写 

代码的一种有效语言。 是与语言无关的,但 开发事实上采用的语言是 C++和 Visual Basic。同Java 

相比,C++在进行  开发时显得更加强大,并可生成更有效的代码,只是它很难使用。Visual Basic 比 

Java 简单得多,但它距离基础操作系统太远了,而且它的对象模型并未实现与  很好的对应(映射)关 

系。Java 是两者之间一种很好的折衷方案。  

接下来,让我们对  开发的一些关键问题进行讨论。编写Java/ 客户和服务器时,这些问题是首先需要 

弄清楚的。  



A。5。1  基础  



 是一种二进制规范,致力于实施可相互操作的对象。例如, 认为一个对象的二进制布局必须能够调用 

另一个 对象里的服务。由于是对二进制布局的一种描述,所以只要某种语言能生成这样的一种布局,就 

可通过它实现 对象。通常,程序员不必关注象这样的一些低级细节,因为编译器可自动生成正确的布 

局。例如,假设您的程序是用 C++写的,那么大多数编译器都能生成符合 规范的一张虚拟函数表格。对 

那些不生成可执行代码的语言,比如VB 和Java,在运行期则会自动挂接到。  

 库也提供了几个基本的函数,比如用于创建对象或查找系统中一个已注册  类的函数。  

一个组件对象模型的基本目标包括:  

■让对象调用其他对象里的服务  

■允许新类型对象(或更新对象)无缝插入环境  

第一点正是面向对象程序设计要解决的问题:我们有一个客户对象,它能向一个服务器对象发出请求。在这 

种情况下,“客户”和“服务器”这两个术语是在常规意义上使用的,并非指一些特定的硬件配置。对于任 

何面向对象的语言,第一个目标都是很容易达到的——只要您的代码是一个完整的代码块,同时实现了服务 

器对象代码以及客户对象代码。若改变了客户和服务器对象相互间的沟通形式,只需简单地重新编译和链接 

一遍即可。重新启动应用程序时,它就会自动采用组件的最新版本。  

但假若应用程序由一些未在自己控制之下的组件对象构成,情况就会变得迥然有异——我们不能控制它们的 

源码,而且它们的更新可能完全独立于我们的应用程序进行。例如,当我们在自己的程序里使用由其他厂商 

开发的ActiveX 控件时,就会面临这一情况。控件会安装到我们的系统里,我们的程序能够(在运行期)定 

位服务器代码,激活对象,同它建立链接,然后使用它。以后,我们可安装控件的新版本,我们的应用程序 

应该仍然能够运行;即使在最糟的情况下,它也应礼貌地报告一条出错消息,比如“控件未找到”等等;一 

般不会莫名其妙地挂起或死机。  

在这些情况下,我们的组件是在独立的可执行代码文件里实现的:DLL 或 EXE。若服务器对象在一个独立的可 

执行代码文件里实现,就需要由操作系统提供的一个标准方法,从而激活这些对象。当然,我们并不想在自 

己的代码里使用DLL 或 EXE 的物理名称及位置,因为这些参数可能经常发生变化。此时,我们想使用的是由 

操作系统维护的一些标识符。另外,我们的应用程序需要对服务器展示出来的服务进行的一个描述。下面这 

两个小节将分别讨论这两个问题。  

  

1。 GUID和注册表  

 采用结构化的整数值(长度为 128位)唯一性地标识系统中注册的 项目。这些数字的正式名称叫作 

GUID (Globally Unique IDentifier,全局唯一标识符),可由特殊的工具生成。此外,这些数字可以保证 

在“任何空间和时间”里独一无二,没有重复。在空间,是由于数字生成器会读取网卡的 ID号码;在时间, 

是由于同时会用到系统的日期和时间。可用GUID 标识 类(此时叫作 CLSID)或者 接口(IID)。尽 

管名字不同,但基本概念与二进制结构都是相同的。GUID 亦可在其他环境中使用,这里不再赘述。  

GUID 以及相关的信息都保存在Windows 注册表中,或者说保存在“注册数据库”(Registration  

Database )中。这是一种分级式的数据库,内建于操作系统中,容纳了与系统软硬件配置有关的大量信息。 

对于,注册表会跟踪系统内安装的组件,比如它们的CLSID、实现它们的可执行文件的名字及位置以及其 

他大量细节。其中一个比较重要的细节是组件的ProgID;ProgID 在概念上类似于GUID,因为它们都标识着 

一个 组件。区别在于 GUID 是一个二进制的、通过算法生成的值。而 ProgID 则是由程序员定义的字串 

值。ProgID 是随同一个CLSID 分配的。  

我们说一个  组件已在系统内注册,最起码的一个条件就是它的 CLSID 和它的执行文件已存在于注册表中 



                                                                662 


…………………………………………………………Page 664……………………………………………………………

 (ProgID 通常也已就位)。在后面的例子里,我们主要任务就是注册与使用 组件。  

注册表的一项重要特点就是它作为客户和服务器对象之间的一个去耦层使用。利用注册表内保存的一些信 

息,客户会激活服务器;其中一项信息是服务器执行模块的物理位置。若这个位置发生了变动,注册表内的 

信息就会相应地更新。但这个更新过程对于客户来说是“透明”或者看不见的。后者只需直接使用ProgID 或 

CLSID 即可。换句话说,注册表使服务器代码的位置透明成为了可能。随着D (分布式)的引入,在 

本地机器上运行的一个服务器甚至可移到网络中的一台远程机器,整个过程甚至不会引起客户对它的丝毫注 

意(大多数情况下如此)。  

  

2。 类型库  

由于 具有动态链接的能力,同时由于客户和服务器代码可以分开独立发展,所以客户随时都要动态侦测 

由服务器展示出来的服务。这些服务是用“类型库”(Type Library)中一种二进制的、与语言无关的形式 

描述的(就象接口和方法签名)。它既可以是一个独立的文件(通常采用。TLB 扩展名),也可以是链接到执 

行程序内部的一种Win32 资源。运行期间,客户会利用类型库的信息调用服务器中的函数。  

我们可以写一个Microsoft Interface Definition Language (微软接口定义语言,MIDL )源文件,用MIDL 

编译器编译它,从而生成一个。TLB 文件。MIDL 语言的作用是对 类、接口以及方法进行描述。它在名称、 

语法以及用途上都类似 OMB/CORBA IDL 。然而,Java 程序员不必使用MIDL 。后面还会讲到另一种不同的 

Microsoft 工具,它能读入Java 类文件,并能生成一个类型库。  

  

3。 :HRESULT 中的函数返回代码  

由服务器展示出来的 函数会返回一个值,采用预先定义好的HRESULT 类型。HRESULT 代表一个包含了三 

个字段的整数。这样便可使用多个失败和成功代码,同时还可以使用其他信息。由于  函数返回的是一个 

HRESULT,所以不能用返回值从函数调用里取回原始数据。若必须返回数据,可传递指向一个内存区域的指 

针,函数将在那个区域里填充数据。我们把这称为 “外部参数”。作为Java/ 程序员,我们不必过于关注 

这个问题,因为虚拟机会帮助我们自动照管一切。这个问题将在后续的小节里讲述。  



A。5。2 MS Java/ 集成  



同C++/ 程序员相比,Microsoft Java 编译器、虚拟机以及各式各样的工具极大简化了Java/ 程序员 

的工作。编译器有特殊的引导命令和包,可将 Java 类当作  类对待。但在大多数情况下,我们只需依赖 

Microsoft JVM 为 提供的支持,同时利用两个有力的外部工具。  

Microsoft Java Virtual Machine (JVM)在 和Java 对象之间扮演了一座桥梁的角色。若将Java 对象创 

建成一个 服务器,那么我们的对象仍然会在 JVM 内部运行。Microsoft JVM 是作为一个 DLL 实现的,它 

向操作系统展示出了 接口。在内部,JVM 将对这些 接口的函数调用映射成 Java 对象中的方法调用。 

当然,JVM 必须知道哪个Java 类文件对应于服务器执行模块;之所以能够找出这方面的信息,是由于我们事 

前已用Javareg 在Windows 注册表内注册了类文件。Javareg 是与Microsoft Java SDK 配套提供的一个工具 

程序,能读入一个 Java 类文件,生成相应的类型库以及一个 GUID,并可将类注册到系统内。亦可用 Javareg 

注册远程服务器。例如,可用它注册在不同机器上运行的一个服务器。  

如果想写一个Java/ 客户,必须经历一系列不同的步骤。Java/ “客户”是一些特殊的Java 代码,它 

们想激活和使用系统内注册的一个  服务器。同样地,虚拟机会与  服务器沟通,并将它提供的服务作 

为Java 类内的各种方法展示(揭示)出来。另一个Microsoft 工具是 jactivex,它能读取一个类型库,并 

生成相应的 Java 源文件,在其中包含特殊的编译器引导命令。生成的源文件属于我们在指定类型库之后命名 

的一个包的一部分。下一步是在自己的 客户Java 源文件中导入那个包。  

接下来让我们讨论两个例子。  



A。5。3  用 Java 设计  服务器  



本节将介绍 ActiveX 控件、Automation 服务器或者其他任何符合 规范的服务器的开发过程。下面这个例 

子实现了一个简单的Automation 服务器,它能执行整数加法。我们用 setAddend()方法设置addend 的值。 

每次调用 sum()方法的时候,addend 就会添加到当前result 里。我们用 getResult()获得result 值,并用 

clear()重新设置值。用于实现这一行为的 Java 类是非常简单的:  

  

public class Adder {  

  private int addend;  



                                                                      663 


…………………………………………………………Page 665……………………………………………………………

  private int result;  

  public void setAddend(int a) { addend = a; }  

  public int getAddend() { return addend; }  

  public int getResult() { return result; }  

  public void sum() { result += addend;  }  

  public void clear() {  

    result = 0;  

    addend = 0;  

  }  

}  

  

为了将这个 Java 类作为一个  对象使用,我们将 Javareg 工具应用于编译好的Adder。class 文件。这个工 

具提供了一系列选项;在这种情况下,我们指定Java 类文件名(〃Adder〃),想为这个服务器在注册表里置 

入的ProgID (〃JavaAdder。Adder。1〃 ),以及想为即将生成的类型库指定的名字(〃JavaAdder。tlb〃)。由于 

尚未给出CLSID,所以 Javareg 会自动生成一个。若我们再次对同样的服务器调用 Javareg,就会直接使用现 

成的CLSID。  

  

javareg /register  

/class:Adder /progid:JavaAdder。Adder。1  

/typelib:JavaAdder。tlb  

  

Javareg 也会将新服务器注册到 Windows 注册表。此时,我们必须记住将 Adder。class 复制到 

WindowsJavatrustlib 目录。考虑到安全方面的原因(特别是涉及程序片调用 服务的问题),只有在 

 服务器已安装到trustlib 目录的前提下,这些服务器才会被激活。  

现在,我们已在自己的系统中安装了一个新的 Automation 服务器。为进行测试,我们需要一个 Automation 

控制器,而 Automation 控制器就是 Visual Basic (VB )。在下面,大家会看到几行VB 代码。按照 VB 的格 

式,我设置了一个文本框,用它从用户那里接收要相加的值。并用一个标签显示结果,用两个下推按钮分别 

调用 sum()和 clear()方法。最
返回目录 上一页 下一页 回到顶部 10 9
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!