使用非java代码

上传人:子 文档编号:43009269 上传时间:2018-06-04 格式:DOC 页数:13 大小:25.60KB
返回 下载 相关 举报
使用非java代码_第1页
第1页 / 共13页
使用非java代码_第2页
第2页 / 共13页
使用非java代码_第3页
第3页 / 共13页
使用非java代码_第4页
第4页 / 共13页
使用非java代码_第5页
第5页 / 共13页
点击查看更多>>
资源描述

《使用非java代码》由会员分享,可在线阅读,更多相关《使用非java代码(13页珍藏版)》请在金锄头文库上搜索。

1、使用非使用非 JAVAJAVA 代码代码使用非 JAVA 代码JAVA 语言及其标准 API(应用程序编程接口)应付应用程序的编写已绰绰有余。但在某些情况下,还是必须使用非 JAVA 编码。例如,我们有时要访问操作系统的专用特性,与特殊的硬件设备打交道,重复使用现有的非 Java 接口,或者要使用“对时间敏感”的代码段,等等。与非 Java 代码的沟通要求获得编译器和“虚拟机”的专门支持,并需附加的工具将 Java 代码映射成非 Java 代码(也有一个简单方法:在第 15 章的“一个 Web 应用”小节中,有个例子解释了如何利用标准输入输出同非 Java 代码连接) 。目前,不同的开发商为我

2、们提供了不同的方案:Java 1.1 有“Java 固有接口”(Java Native Interface,JNI) ,网景提出了自己的“Java 运行期接口” (Java Runtime Interface)计划,而微软提供了J/Direct、 “本源接口” (Raw Native Interface,RNI)以及Java/COM 集成方案。各开发商在这个问题上所持的不同态度对程序员是非常不利的。若 Java 应用必须调用固有方法,则程序员或许要实现固有方法的不同版本具体由应用程序运行的平台决定。程序员也许实际需要不同版本的 Java 代码,以及不同的 Java 虚拟机。另一个方案是 COR

3、BA(通用对象请求代理结构) ,这是由OMG(对象管理组,一家非赢利性的公司协会)开发的一种集成技术。CORBA 并非任何语言的一部分,只是实现通用通信总线及服务的一种规范。利用它可在由不同语言实现的对象之间实现“相互操作”的能力。这种通信总线的名字叫作 ORB(对象请求代理) ,是由其他开发商实现的一种产品,但并不属于 Java 语言规范的一部分。Java 固有接口JNI 是一种包容极广的编程接口,允许我们从 Java 应用程序里调用固有方法。它是在 Java 1.1 里新增的,维持着与 Java 1.0 的相应特性“固有方法接口” (NMI)某种程度的兼容。NMI设计上一些特点使其未获所有

4、虚拟机的支持。考虑到这个原因,Java 语言将来的版本可能不再提供对 NMI 的支持,这儿也不准备讨论它。目前,JNI 只能与用 C 或 C+写成的固有方法打交道。利用JNI,我们的固有方法可以:创建、检查及更新 Java 对象(包括数组和字串)调用 Java 方法俘获和丢弃“异常”装载类并获取类信息进行运行期类型检查所以,原来在 Java 中能对类及对象做的几乎所有事情在固有方法中同样可以做到。调用固有方法先从一个简单的例子开始:一个 Java 程序调用固有方法,后者再调用 Win32 的 API 函数 MessageBox(),显示出一个图形化的文本框。这个例子稍后也会与 J/Direct

5、 一志使用。若您的平台不是Win32,只需将包含了下述内容的 C 头:#include 替换成:#include 并将对 MessageBox()的调用换成调用 printf()即可。第一步是写出对固有方法及它的自变量进行声明的 Java 代码:在固有方法声明的后面,跟随有一个 static 代码块,它会调用 System.loadLibrary()(可在任何时候调用它,但这样做更恰当)System.loadLibrary()将一个 DLL 载入内存,并建立同它的链接。DLL 必须位于您的系统路径,或者在包含了 Java 类文件的目录中。根据具体的平台,JVM 会自动添加适当的文件扩展名。1.

6、 C 头文件生成器:javah现在 Java 源文件,并对编译出来的.class 文件运行 javah。javah是在 1.0 版里提供的,但由于要使用 Java 1.1 JNI,所以必须指定-jni 参数:javah -jni ShowMsgBoxjavah 会读入类文件,并为每个固有方法声明在 C 或 C+头文件里生成一个函数原型。从“#ifdef_cplusplus”这个预处理引导命令可以看出,该文件既可由 C 编译器编译,亦可由 C+编译器编译。第一个#include命令包括 jni.h一个头文件,作用之一是定义在文件其余部分用到的类型;JNIEXPORT 和 JNICALL 是一些宏

7、,它们进行了适当的扩充,以便与那些不同平台专用的引导命令配合;JNIEnv,jobject以及 jstring 则是 JNI 数据类型定义。2. 名称管理和函数签名JNI 统一了固有方法的命名规则;这一点是非常重要的,因为它属于虚拟机将 Java 调用与固有方法链接起来的机制的一部分。从根本上说,所有固有方法都要以一个“Java”起头,后面跟随 Java方法的名字;下划线字符则作为分隔符使用。若 Java 固有方法“过载” (即命名重复) ,那么也把函数签名追加到名字后面。在原型前面的注释里,大家可看到固有的签名。欲了解命名规则和固有方法签名更详细的情况,请参考相应的 JNI 文档。3. 实现

8、自己的 DLL写一个 C 或 C+源文件,在其中包含由 javah 生成的头文件;并实现固有方法;然后编译它,生成一个动态链接库。这一部分的工作是与平台有关的,所以我假定读者已经知道如何创建一个DLL。通过调用一个 Win32 API,下面的代码实现了固有方法。随后,它会编译和链接到一个名为 MsgImpl.dll 的文件里。若对 Win32 没有兴趣,只需跳过 MessageBox()调用;最有趣的部分是它周围的代码。传递到固有方法内部的自变量是返回 Java 的大门。第一个自变量是类型 JNIEnv 的,其中包含了回调 JVM 需要的所有挂钩。由于方法的类型不同,第二个自变量也有自己不同的

9、含义。对于象上例那样的非 static 方法(也叫作实例方法) ,第二个自变量等价于 C+的“this”指针,并类似于 Java 的“this”:都引用了调用固有方法的那个对象。对于 static 方法,它是对特定Class 对象的一个引用,方法就是在那个 Class 对象里实现的。剩余的自变量代表传递到固有方法调用里的 Java 对象。主类型也是以这种形式传递的,但它们进行的“按值”传递。访问 JNI 函数:JNIEnv 自变量利用 JNI 函数,程序员可从一个固有方法的内部与 JVM 打交道。正如大家在前面的例子中看到的那样,每个 JNI 固有方法都会接收一个特殊的自变量作为自己的第一个参

10、数:JNIEnv 自变量它是指向类型为 JNIEnv_的一个特殊 JNI 数据结构的指针。JNI 数据结构的一个元素是指向由 JVM 生成的一个数组的指针;该数组的每个元素都是指向一个 JNI 函数的指针。可从固有方法的内部发出对 JNI函数的调用,做法是撤消对这些指针的引用。每种 JVM 都以自己的方式实现了 JNI 函数,但它们的地址肯定位于预先定义好的偏移处。利用 JNIEnv 自变量,程序员可访问一系列函数。这些函数可划分为下述类别:获取版本信息进行类和对象操作控制对 Java 对象的全局和局部引用访问实例字段和静态字段调用实例方法和静态方法执行字串和数组操作产生和控制 Java 异常

11、若观察一下 jni.h 头文件,就会发现在#ifdef _cplusplus 预处理器条件的内部,当由 C+编译器编译时,JNIEnv_结构被定义成一个类。这个类包含了大量内嵌函数。通过一种简单而且熟悉的语法,这些函数让我们可以从容访问 JNI 函数。例如,前例包含了下面这行代码:(*jEnv)-ReleaseStringUTFChars(jEnv, jMsg,msg);它在 C+里可改写成下面这个样子:jEnv-ReleaseStringUTFChars(jMsg,msg);1. 访问 Java 字串作为访问 JNI 函数的一个例子,请思考上述的代码。利用JNIEnv 的自变量 jEnv 来

12、访问一个 Java 字串。Java 字串采取的是Unicode 格式,所以假若收到这样一个字串,并想把它传给一个非Unicode 函数(如 printf()) ,首先必须用 JNI 函数GetStringUTFChars()将其转换成 ASCII 字符。该函数能接收一个Java 字串,然后把它转换成 UTF-8 字符(用 8 位宽度容纳 ASCII 值,或用 16 位宽度容纳 Unicode;若原始字串的内容完全由 ASCII 构成,那么结果字串也是 ASCII) 。GetStringUTFChars 是 JNIEnv 间接指向的那个结构里的一个字段,而这个字段又是指向一个函数的指针。为访问

13、JNI 函数,用传统的 C 语法来调用一个函数(通过指针) 。利用上述形式可实现对所有 JNI 函数的访问。传递和使用 Java 对象为传递对象,声明固有方法时要采用原始的 Java 语法。如下例所示,MyJavaClass 有一个 public(公共)字段,以及一个 public方法。UseObjects 类声明了一个固有方法,用于接收 MyJavaClass类的一个对象。为调查固有方法是否能控制自己的自变量,设置自变量的 public 字段,调用固有方法,然后打印出 public 字段的值。编译好代码,并将.class 文件传递给 javah 后,就可以实现固有方法。除第一个自变量外,C+

14、函数会接收一个 jobject,它代表Java 对象引用“固有”的那一面那个引用是从 Java 代码里传递的。简单地读取 aValue,把它打印出来,改变这个值,调用对象的 divByTwo()方法,再将值重新打印一遍。为访问一个字段或方法,首先必须获取它的标识符。利用适当的 JNI 函数,可方便地取得类对象、元素名以及签名信息。这些函数会返回一个标识符,利用它可访问对应的元素。尽管这一方式显得有些曲折,但固有方法确实对 Java 对象的内部布局一无所知。因此,它必须通过由 JVM 返回的索引访问字段和方法。这样一来,不同的 JVM 就可实现不同的内部对象布局,同时不会对固有方法造成影响。若运

15、行 Java 程序,就会发现从 Java 那一侧传来的对象是由我们的固有方法处理的。但传递的到底是什么呢?是指针,还是 Java引用?而且垃圾收集器在固有方法调用期间又在做什么呢?垃圾收集器会在固有方法执行期间持续运行,但在一次固有方法调用期间,我们的对象可保证不会被当作“垃圾”收集去。为确保这一点,事先创建了“局部引用” ,并在固有方法调用之后立即清除。由于它们的“生命期”与调用过程息息相关,所以能够保证对象在固有方法调用期间的有效性。由于这些引用会在每次函数调用的时候创建和破坏,所以不可在 static 变量中制作固有方法的局部副本(本地拷贝) 。若希望一个引用在函数存在期间持续有效,就需

16、要一个全局引用。全局引用不是由 JVM 创建的,但通过调用特定的 JNI 函数,程序员可将局部引用扩展为全局引用。创建一个全局引用时,需对引用对象的“生存时间”负责。全局引用(以及它引用的对象)会一直留在内存里,直到用特定的 JNI 函数明确释放了这个引用。它类似于 C 的malloc()和 free()。JNI 和 Java 异常利用 JNI,可丢弃、捕捉、打印以及重新丢弃 Java 异常,就象在一个 Java 程序里那样。但对程序员来说,需自行调用专用的 JNI 函数,以便对异常进行处理。下面列出用于异常处理的一些 JNI 函数:Throw():丢弃一个现有的异常对象;在固有方法中用于重新丢弃一个异常。ThrowNew():生成一个新的异常对象,并将其丢弃。ExceptionOccurred():判断一个异常是否已被丢弃,但尚未清除。ExceptionDescribe():打印一个异常和堆栈跟踪信息。ExceptionClear():清除一个待决的异常。FatalError():

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 生活休闲 > 科普知识

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号