- 写在前面的话
- 引言
- 第 1 章 对象入门
- 第 2 章 一切都是对象
- 第 3 章 控制程序流程
- 第 4 章 初始化和清除
- 第 5 章 隐藏实施过程
- 第 6 章 类再生
- 第 7 章 多形性
- 第 8 章 对象的容纳
- 第 9 章 违例差错控制
- 第 10 章 Java IO 系统
- 第 11 章 运行期类型鉴定
- 第 12 章 传递和返回对象
- 第 十三 章 创建窗口和程序片
- 第 14 章 多线程
- 第 15 章 网络编程
- 第 16 章 设计范式
- 第 17 章 项目
- 附录 A 使用非 JAVA 代码
- 附录 B 对比 C++和 Java
- 附录 C Java 编程规则
- 附录 D 性能
- 附录 E 关于垃圾收集的一些话
- 附录 F 推荐读物
A.1.1 调用固有方法
我们先从一个简单的例子开始:一个 Java 程序调用固有方法,后者再调用 Win32 的 API 函数 MessageBox(),显示出一个图形化的文本框。这个例子稍后也会与 J/Direct 一志使用。若您的平台不是 Win32,只需将包含了下述内容的 C 头:
#include <windows.h>
替换成:
#include <stdio.h>
并将对 MessageBox() 的调用换成调用 printf() 即可。
第一步是写出对固有方法及它的自变量进行声明的 Java 代码:
class ShowMsgBox { public static void main(String [] args) { ShowMsgBox app = new ShowMsgBox(); app.ShowMessage("Generated with JNI"); } private native void ShowMessage(String msg); static { System.loadLibrary("MsgImpl"); } }
在固有方法声明的后面,跟随有一个 static 代码块,它会调用 System.loadLibrary()(可在任何时候调用它,但这样做更恰当)System.loadLibrary() 将一个 DLL 载入内存,并建立同它的链接。DLL 必须位于您的系统路径,或者在包含了 Java 类文件的目录中。根据具体的平台,JVM 会自动添加适当的文件扩展名。
1. C 头文件生成器:javah
现在编译您的 Java 源文件,并对编译出来的.class 文件运行 javah。javah 是在 1.0 版里提供的,但由于我们要使用 Java 1.1 JNI,所以必须指定-jni 参数:
javah -jni ShowMsgBox
javah 会读入类文件,并为每个固有方法声明在 C 或 C++头文件里生成一个函数原型。下面是输出结果——ShowMsgBox.h 源文件(为符合本书的要求,稍微进行了一下修改):
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class ShowMsgBox */ #ifndef _Included_ShowMsgBox #define _Included_ShowMsgBox #ifdef __cplusplus extern "C" { #endif /* * Class: ShowMsgBox * Method: ShowMessage * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_ShowMsgBox_ShowMessage (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif
从“#ifdef_cplusplus”这个预处理引导命令可以看出,该文件既可由 C 编译器编译,亦可由 C++编译器编译。第一个#include 命令包括 jni.h——一个头文件,作用之一是定义在文件其余部分用到的类型;JNIEXPORT 和 JNICALL 是一些宏,它们进行了适当的扩充,以便与那些不同平台专用的引导命令配合;JNIEnv,jobject 以及 jstring 则是 JNI 数据类型定义。
2. 名称管理和函数签名
JNI 统一了固有方法的命名规则;这一点是非常重要的,因为它属于虚拟机将 Java 调用与固有方法链接起来的机制的一部分。从根本上说,所有固有方法都要以一个“Java”起头,后面跟随 Java 方法的名字;下划线字符则作为分隔符使用。若 Java 固有方法“过载”(即命名重复),那么也把函数签名追加到名字后面。在原型前面的注释里,大家可看到固有的签名。欲了解命名规则和固有方法签名更详细的情况,请参考相应的 JNI 文档。
3. 实现自己的 DLL
此时,我们要做的全部事情就是写一个 C 或 C++源文件,在其中包含由 javah 生成的头文件;并实现固有方法;然后编译它,生成一个动态链接库。这一部分的工作是与平台有关的,所以我假定读者已经知道如何创建一个 DLL。通过调用一个 Win32 API,下面的代码实现了固有方法。随后,它会编译和链接到一个名为 MsgImpl.dll 的文件里:
#include <windows.h> #include "ShowMsgBox.h" BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void** lpReserved) { return TRUE; } JNIEXPORT void JNICALL Java_ShowMsgBox_ShowMessage(JNIEnv * jEnv, jobject this, jstring jMsg) { const char * msg; msg = (*jEnv)->GetStringUTFChars(jEnv, jMsg,0); MessageBox(HWND_DESKTOP, msg, "Thinking in Java: JNI", MB_OK | MB_ICONEXCLAMATION); (*jEnv)->ReleaseStringUTFChars(jEnv, jMsg,msg); }
若对 Win32 没有兴趣,只需跳过 MessageBox() 调用;最有趣的部分是它周围的代码。传递到固有方法内部的自变量是返回 Java 的大门。第一个自变量是类型 JNIEnv 的,其中包含了回调 JVM 需要的所有挂钩(下一节再详细讲述)。由于方法的类型不同,第二个自变量也有自己不同的含义。对于象上例那样的非 static 方法(也叫作实例方法),第二个自变量等价于 C++的“this”指针,并类似于 Java 的“this”:都引用了调用固有方法的那个对象。对于 static 方法,它是对特定 Class 对象的一个引用,方法就是在那个 Class 对象里实现的。
剩余的自变量代表传递到固有方法调用里的 Java 对象。主类型也是以这种形式传递的,但它们进行的“按值”传递。
在后面的小节里,我们准备讲述如何从一个固有方法的内部访问和控制 JVM,同时对上述代码进行更详尽的解释。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论