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

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

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




 “垃圾”收集去。为确保这一点,事先创建了“局部引用”,并在固有方法调用之后立即清除。由于它们的 

 “生命期”与调用过程息息相关,所以能够保证对象在固有方法调用期间的有效性。  

由于这些引用会在每次函数调用的时候创建和破坏,所以不可在static 变量中制作固有方法的局部副本(本 

地拷贝)。若希望一个引用在函数存在期间持续有效,就需要一个全局引用。全局引用不是由JVM 创建的, 

但通过调用特定的 JNI 函数,程序员可将局部引用扩展为全局引用。创建一个全局引用时,需对引用对象的 

 “生存时间”负责。全局引用(以及它引用的对象)会一直留在内存里,直到用特定的JNI 函数明确释放了 

这个引用。它类似于C 的malloc()和 free()。  



A。1。4 JNI 和 Java 异常  



利用 JNI,可丢弃、捕捉、打印以及重新丢弃Java 异常,就象在一个 Java 程序里那样。但对程序员来说, 

需自行调用专用的JNI 函数,以便对异常进行处理。下面列出用于异常处理的一些JNI 函数:  

■Throw():丢弃一个现有的异常对象;在固有方法中用于重新丢弃一个异常。  

■ThrowNew():生成一个新的异常对象,并将其丢弃。  

■ExceptionOccurred():判断一个异常是否已被丢弃,但尚未清除。  

■ExceptionDescribe():打印一个异常和堆栈跟踪信息。  

■ExceptionClear():清除一个待决的异常。  

■FatalError():造成一个严重错误,不返回。  

在所有这些函数中,最不能忽视的就是ExceptionOccurred()和ExceptionClear()。大多数JNI 函数都能产 

生异常,而且没有象在 Java 的try 块内的那种语言特性可供利用。所以在每一次 JNI 函数调用之后,都必须 

调用ExceptionOccurred(),了解异常是否已被丢弃。若侦测到一个异常,可选择对其加以控制(可能时还 

要重新丢弃它)。然而,必须确保异常最终被清除。这可以在自己的函数中用ExceptionClear()来实现;若 

异常被重新丢弃,也可能在其他某些函数中进行。但无论如何,这一工作是必不可少的。  



                                                                               654 


…………………………………………………………Page 656……………………………………………………………

我们必须保证异常被彻底清除。否则,假若在一个异常待决的情况下调用一个JNI 函数,获得的结果往往是 

无法预知的。也有少数几个JNI 函数可在异常时安全调用;当然,它们都是专门的异常控制函数。  



A。1。5 JNI 和线程处理  



由于Java 是一种多线程语言,几个线程可能同时发出对一个固有方法的调用(若另一个线程发出调用,固有 

方法可能在运行期间暂停)。此时,完全要由程序员来保证固有调用在多线程的环境中安全进行。例如,要 

防范用一种未进行监视的方法修改共享数据。此时,我们主要有两个选择:将固有方法声明为“同步”,或 

在固有方法内部采取其他某些策略,确保数据处理正确地并发进行。  

此外,绝对不要通过线程传递 JNIEnv,因为它指向的内部结构是在“每线程”的基础上分配的,而且包含了 

只对那些特定的线程才有意义的信息。  



A。1。6  使用现成代码  



为实现JNI 固有方法,最简单的方法就是在一个Java 类里编写固有方法的原型,编译那个类,再通过 javah 

运行。class 文件。但假若我们已有一个大型的、早已存在的代码库,而且想从Java 里调用它们,此时又该 

如何是好呢?不可将DLL 中的所有函数更名,使其符合 JNI 命名规则,这种方案是不可行的。最好的方法是 

在原来的代码库“外面”写一个封装DLL。Java 代码会调用新 DLL 里的函数,后者再调用原始的DLL 函数。 

这个方法并非仅仅是一种解决方案;大多数情况下,我们甚至必须这样做,因为必须面向对象引用调用 JNI 

函数,否则无法使用它们。  



A。2 微软的解决方案  



到本书完稿时为止,微软仍未提供对JNI 的支持,只是用自己的专利方法提供了对非Java 代码调用的支持。 

这一支持内建到编译器 Microsoft JVM 以及外部工具中。只有程序用 Microsoft Java 编译器编译,而且只有 

在Microsoft Java 虚拟机(JVM)上运行的时候,本节讲述的特性才会有效。若计划在因特网上发行自己的 

应用,或者本单位的内联网建立在不同平台的基础上,就可能成为一个严重的问题。  

微软与Win32 代码的接口为我们提供了连接 Win32 的三种途径:  

(1) J/Direct:方便调用Win32 DLL 函数的一种途径,具有某些限制。  

(2) 本原接口(RNI):可调用Win32 DLL 函数,但必须自行解决“垃圾收集”问题。  

(3) Java/ 集成:可从 Java 里直接揭示或调用  服务。  

后续的小节将分别探讨这三种技术。  

写作本书的时候,这些特性均通过了Microsoft SDK for Java 2。0 beta 2 的支持。可从微软公司的Web 站 

点下载这个开发平台(要经历一个痛苦的选择过程,他们叫作“Active Setup”)。Java SDK 是一套命令行 

工具的集合,但编译引擎可轻易嵌入Developer Studio 环境,以便我们用 Visual J++ 1。1 来编译 Java 1。1 

代码。  



A。3 J/Direct   



J/Direct 是调用 Win32 DLL 函数最简单的方式。它的主要设计目标是与Win32API 打交道,但完全可用它调 

用其他任何 API。但是,尽管这一特性非常方便,但它同时也造成了某些限制,且降低了性能(与RNI 相 

比)。但J/Direct 也有一些明显的优点。首先,除希望调用的那个DLL 里的代码之外,没有必要再编写额外 

的非Java 代码,换言之,我们不需要一个封装器或者代理/存根DLL。其次,函数自变量与标准数据类型之 

间实现了自动转换。若必须传递用户自定义的数据类型,那么 J/Direct 可能不按我们的希望工作。第三,就 

象下例展示的那样,它非常简单和直接。只需少数几行,这个例子便能调用Win32 API 函数MessageBox(), 

它能弹出一个小的模态窗口,并带有一个标题、一条消息、一个可选的图标以及几个按钮。  

  

public class ShowMsgBox {  

  public static void main(String args'')   

  throws UnsatisfiedLinkError   {  

    MessageBox(0;  

      〃Created by the MessageBox() Win32 func〃;  

      〃Thinking in Java〃; 0);  

  }  



                                                                       655 


…………………………………………………………Page 657……………………………………………………………

  /** @dll。import(〃USER32〃) */  

  private static native int   

  MessageBox(int hwndOwner; String text;  

    String title; int fuStyle);  

}  

  

令人震惊的是,这里便是我们利用 J/Direct 调用Win32 DLL 函数所需的全部代码。其中的关键是位于示范代 

码底部的MessageBox()声明之前的@dll 。import 引导命令。它表面上看是一条注释,但实际并非如此。它的 

作用是告诉编译器:引导命令下面的函数是在 USER32 DLL 里实现的,而且应相应地调用。我们要做的全部事 

情就是提供与DLL 内实现的函数相符的一个原型,并调用函数。但是毋需在Java 版本里手工键入需要的每一 

个Win32 API 函数,一个Microsoft Java 包会帮我们做这件事情(很快就会详细解释)。为了让这个例子正 

常工作,函数必须“按名称”由DLL 导出。但是,也可以用@dll。import 引导命令“按顺序”链接。举个例 

子来说,我们可指定函数在DLL 里的入口位置。稍后还会具体讲述@dll。import 引导命令的特性。  

用非Java 代码进行链接的一个重要问题就是函数参数的自动配置。正如大家看到的那样,MessageBox()的 

Java 声明采用了两个字串自变量,但原来的C 方案则采用了两个 char 指针。编译器会帮助我们自动转换标 

准数据类型,同时遵照本章后一节要讲述的规则。  

最好,大家或许已注意到了main()声明中的 UnsatisfiedLinkError 异常。在运行期的时候,一旦链接程序 

不能从非Java 函数里解析出符号,就会触发这一异常(事件)。这可能是由多方面的原因造成的:。dll 文 

件未找到;不是一个有效的DLL;或者J/Direct 未获您所使用的虚拟机的支持。为了使DLL 能被找到,它必 

须位于Windows 或WindowsSystem 目录下,位于由PATH 环境变量列出的一个目录中,或者位于和。class 文 

件相同的目录。J/Direct 获得了 Microsoft Java 编译器 1。02。4213 版本及更高版本的支持,也获得了 

Microsoft JVM 4。79。2164 及更高版本的支持。为了解自己编译器的版本号,请在命令行下运行 JVC,不要加 

任何参数。为了解 JVM 的版本号,请找到msjava。dll 的图标,并利用右键弹出菜单观察它的属性。  



A。3。1 @dll。import 引导命令  



作为使用J/Direct 唯一的途径,@dll。import 引导命令相当灵活。它提供了为数众多的修改符,可用它们自 

定义同非Java 代码建立链接关系的方式。它亦可应用于类内的一些方法,或应用于整个类。也就是说,我们 

在那个类内声明的所有方法都是在相同的 DLL 里实现的。下面让我们具体研究一下这些特性。  

  

1。 别名处理和按顺序链接  

为了使@dll。import 引导命令能象上面显示的那样工作,DLL 内的函数必须按名字导出。然而,我们有时想使 

用与DLL 里原始名字不同的一个名字(别名处理),否则函数就可能按编号(比如按顺序)导出,而不是按 

名字导出。下面这个例子声明了FinestraDiMessaggio() (用意大利语说的“MessageBox”)。正如大家看 

到的那样,使用的语法是非常简单的。  

  

public class Aliasing {  

  public static void main(String args'')   

  throws UnsatisfiedLinkError   {  

    FinestraDiMessaggio(0;  

      〃Created by the MessageBox() Win32 func〃;  

      〃Thinking in Java〃; 0);  

  }  

  /** @dll。import(〃USER32〃;   

  entrypoint=〃MessageBox〃) */  

  private static native int   

  FinestraDiMessaggio(int hwndOwner; String text;  

    String title; int fuStyle);  

}  

  

下面这个例子展示了如何同DLL 里并非按名字导出的一个函数建立链接,那个函数事实是按它们在DLL
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!