在Java程序中调用C函数--打印"HelloWorld"
2011年05月02日
源地址:http://java.sun.com/docs/books/jni/html/start.html #26346
本文是将书中的第二章单独抽出来,红色部分为译者注.
1.概述
这个打印的过程是用JDK或Java 2 SDK写一个简单的Java程序,程序会调用一个C函数打印"HelloWorld".这个过程将包括以下步骤: 创建一个Java类(HelloWorld.java),以及定义一个native方法.
使用javac去编译这个HelloWorld源文件,生成HelloWorld.class.
使用javah jni 来生成C的头文件(HelloWorld.h),这个头文件包含native方法的实现原型.javah是JDK或者Java 2 SDK提供的一个工具.
编写C的代码(HelloWorld.c),实现这个native方法.
把这个C的实现编译到Java native库中,创建HelloWorld.dll或者libHello-World.so.
使用Java运行时解释器运行HelloWorld程序.无论是类文件(HelloWorld.class),还是native库(HelloWorld.dll或者libHelloWorld.so),都是在运行时加载.
剩下的部分将详细介绍这些步骤:
2.Native方法声明
用Java语言写下面的程序.该程序定义的类名是HelloWorld,包含一个native方法,print. 该类不在任何包中
HelloWorld的类定义用一个print的本地方法开始.其次就是main方法,它实例化了一个HelloWorld,并且调用这个实例的print的本地方法.类定义的最后部分是一个静态代码块,它加载了包含print本地方法实现的native库.
在native方法(例如print)的定义和常规的Java语言方法的定义中有这样的两个差异.一个本地方法声明必须包含native修饰符,native修饰符表明,该方法以另一种语言实现.此外,本地方法的定义是以分号结束,因为在类本身中,没有实现本地方法,我们将在一个独立的C文件中实现这个print方法.
在调用本地方法print之前,native库必须要加载print的实现.在这种情况下,我们在HelloWorld类的静态代码块中加载native库,Java虚拟机会自动运行静态代码块,并且在调用任何HelloWorld类的方法之前,以确保native库在print本地方法调用之前就已经加载了.
我们定义一个main方法能去运行HelloWorld,HelloWorld.main调用本地方法打印,就像是调用常规方法一样.
System.loadLibrary需要一个库名,然后定位到符合这个名字的native库中,并加载到应用程序的native库.我们将在本书的后面讨论具体的加载过程.现在只需要简单地记住,为了System.loadLibrary("HelloWorld")成功,我们需要创建一个native库,用来调用Win32上的HelloWorld.dll或者在Solaris上的libHelloWorld.so.
3.编译HelloWorld
在你定义HelloWorld类之后,保存源代码为文件HelloWorld.java,然后用javac 编译器编译源码:
javac HelloWorld.java
这个命令将在当前目录生成一个HelloWorld.class文件.
4.创建Native方法的头文件
下一步,我们将使用javah工具来生成一个JNI风格(JNI-style)的头文件.当在C 上面实现本地方法时很有用.你可以在HelloWorld.class上运行javah,就像下面一样:
javah jni HelloWorld
生成的头文件的名字是在类名后面追加一个".h".上面显示的命令将生成一个文件名为HelloWorld.h的文件.我们将不在这里列出生成的头文件.这个头文件最重要的部分Java_HelloWorld_print的函数原型,这是C 函数实现的HelloWorld.print方法:
JNIEXPORT void JNICALL
Java_HelloWorld_print (JNIEnv *, jobject); 现在忽略JNIEXPORT和JNICALL宏.你可能已经注意到,本地方法C 的实现接收了两个参数,然而实际上对应的native方法声明不接收任何参数.对于每一个本地方法实现,第一个参数都是JNIEnv接口指针(JNIEnv interface pointer),第二个参数是一个指向HelloWorld对象本身(有点像C++的"this"指针)的引用.我们将在本书的后面讨论如何使用JNIEnv接口指针和jobject.但是现在这个简单的例子就忽略这两个参数. 5.写本地方法的实现
用javah生成的JNI风格的头文件能够帮助你去完成C/C++的本地方法实现.你写的函数必须遵循生成的头文件的原型.你能在C 文件HelloWorld.c中实现HelloWorld.print方法,就像下面一样: 该本地方法的实现很简单,它使用了printf函数来显示字符串"HelloWorld!",然后返回.正如前面提到,两个参数,JNIEnv指针和指向对象的引用,都被忽略了.
这个C 程序包含了三个头文件: jni.h--这个头文件提供了本地代码需要调用JNI函数的信息.当写本地方法的时候,你必须在你的C 或者 C++源文件中,永远包含这个文件.
stdio.h--以上代码段也包含了stdio.h ,因为它使用了printf函数.
HelloWorld.h--这是你用javah生成的头文件,它包含了Java_HelloWorld_print的函数原型.
6.编译C 源码和创建native库
请记住,当你在HelloWorld.java创建HelloWorld.class的时候,你包含了一行加载native库的代码在你的程序里面:
System.loadLibrary("HelloWorld");
现在所有必要的C 代码已经写完了,你需要去编译HelloWorld.c和创建这个native库.
不同的操作系统支持不同的方法来建立native库,在Solaris上面,下面的命令生成一个共享库调用libHelloWorld.so: -G选项指示这个C 编译器去生成一个共享库,而不是一个普通的Solaris的可执行文件.因为本书页面宽度的限制,我把一行分成了两行,你需要在一行输入命令,或者把命令放在脚本中.在Win32的系统上,下面的命令将使用Microsoft Visual C++ 编译器建立一个动态链接库(DLL)HelloWorld.dll: -MD选项将确保HelloWorld.dll已经连接到Win32的多线程C 库当中.-LD选项指示C 编译器去生成一个DLL,而不是一个普通的Win32可执行文件.当然,在Solaris和Win32,你都需要放到include路径中去,以反射你自己的安装配置.
-I把指定路径加到include的搜索路径中.
如果遇到"Cannot open include file:'jni.h'"的错误,则说明路径指定地有问题.有两种解决方案:
1.在你的JDK安装目录下有个include目录,把里面的jni.h和win32目录下的jawt_md.h和jni.md.h复制到你的C 编译器的include目录中(可能是vc\include).
2.指定正确的搜索路径.比如我的JDK安装在F:\Program Files\Java\jdk1.6.0_16\,那么上面的参数就改为:
-I"F:\Program Files\Java\jdk1.6.0_16\include" -I"F:\Program Files\Java\jdk1.6.0_16\include\win32"
如果目录中有空格要引起来.
7 运行程序
此时,你要准备好两个组件去运行这个程序.类文件(HelloWorld.class)调用本地方法,native库(HelloWorld.dll)实现这个本地方法.
因为HelloWorld类包含了它自己的main方法,你可以在Solaris 或者是Win32的操作系统上运行这个程序:
java HelloWorld
你应该会看到下列的输出:
Hello World!
设置好正确地native库路径对程序的运行是很重要的.native库路径是一个目录列表,是Java虚拟机装载native库时的搜索列表.如果你没有设置一个正确的native库路径,你就会看到类似于下列的错误:
java.lang.UnsatisfiedLinkError: no HelloWorld in library path
at java.lang.Runtime.loadLibrary(Runtime.java)
at java.lang.System.loadLibrary(System.java)
at HelloWorld.main(HelloWorld.java) 要确保native库存在于native库路径的目录中.如果你是在Solaris上运行,LD_LIBRARY_PATH的环境变量是用来定义native库路径.请确保它包含的目录名称中包含了libHelloWorld.so 文件.如果libHelloWorld.so 文件在当前目录里面,你可以在标准shell(sh)或者KornShell(ksh)上发出两条命令去设置正确的LD_LIBRARY_PATH环境变量属性: 这与在C shell(csh或者tcsh)中的下列命令等效: 如果你是在Windows95或者Windows NT 计算机上运行,设法确保HelloWorld.dll是在当前目录下,或者在PATH环境变量列出的目录中.
在Java 2 SDK 1.2版本中,你也可以在java命令行中像系统属性一样指定native库路径,如下所述: -D 命令行选项是设置Java平台的系统属性,设置java.library.path属性成".",表示JVM要搜索当前目录下的native库. 如果本文有任何问题,请及时指出,以免对后来者产生不必要的困扰,不胜感激!
发表评论
-
Android 开源项目收集(开源之路险且艰,有负出就有收获)
2012-01-20 10:15 740Android 开源项目收集(开 ... -
Android 目录结构
2012-01-20 10:15 740Android 目录结构 2010年11月08日 In ... -
一个对Winsock完成端口模型封装的类
2012-01-20 10:15 615一个对Winsock完成端口模型封装的类 2011年01月0 ... -
给正准备学习VC++朋友的建议
2012-01-20 10:15 570给正准备学习VC++朋友的 ... -
WinCE驱动开发问题精华集锦(一)
2012-01-19 15:18 474WinCE驱动开发问题精华 ... -
电脑天书(九)
2012-01-19 15:18 579电脑天书(九) 2011年04月 ... -
MaxDOS 8.0 发布,全面支持WIN7及X64系统
2012-01-19 15:17 1069MaxDOS 8.0 发布,全面支持WIN7及X64系统 2 ... -
单机MySQL数据库的优化
2012-01-19 15:17 683单机MySQL数据库的优化 ... -
DOS的命令
2012-01-19 15:17 585DOS的命令 2011年04月23日 先介绍一下通配符的 ... -
杨建:网站加速--服务器编写篇(上)
2012-01-17 05:03 689杨建:网站加速--服务器 ... -
sqlplus常用命令使用3
2012-01-17 05:03 677sqlplus常用命令使用3 2011年04月24日 t ... -
LoadRunner监视的性能计数器
2012-01-17 05:02 604LoadRunner监视的性能计数器 2011年10月12日 ... -
关于PHP的缓存
2012-01-17 05:02 644关于PHP的缓存 2011年05 ... -
PHP实现多web服务器共享SESSION数据
2012-01-17 05:02 652PHP实现多web服务器共享S ... -
Eclipse 上svn插件安装
2012-01-16 03:55 778Eclipse 上svn插件安装 2009年09月16日 ... -
打开旧的项目文件还有注意事项
2012-01-16 03:55 614打开旧的项目文件还有注意事项 2010年05月30日 m ... -
eclipse及插件安装基础教程
2012-01-16 03:55 901eclipse及插件安装基础教程 2010年04月22日 ... -
ClassLoader整理总结
2012-01-16 03:55 586ClassLoader整理总结 2010年10月16日 ... -
MyEclipse7.5将Java程序打包成*.exe
2012-01-16 03:55 731MyEclipse7.5将Java程序打包成*.exe 20 ...
相关推荐
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...
II. java c/cpp互相调用实例(姊妹篇之一)——java调用c/cpp 4 一 先制作一个系统中有的DLL文件(cpp给出的sdk接口) 4 二 JNI 7 1、 编写java文件 7 2、 生成.h头文件 8 3、 用c/cpp实现这个头文件 9 三 测试 10 ...
浮动的广告 嵌套在html中 各种EJB之间的调用示例 7个目标文件 摘要:Java源码,初学实例,EJB调用实例 各种EJB之间的调用源码示例,用远程接口的引用访问EJB、函数将被FirstEJB调用,同时它将调用secondEJB 基于JAVA的...
浮动的广告 嵌套在html中 各种EJB之间的调用示例 7个目标文件 摘要:Java源码,初学实例,EJB调用实例 各种EJB之间的调用源码示例,用远程接口的引用访问EJB、函数将被FirstEJB调用,同时它将调用secondEJB 基于JAVA的...
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java...
程序可以采用 JAVA 开发,但是因为它的虚拟机 (Virtual Machine) Dalvik ,是将 JAVA 的 bytecode 转成 自 己的格式,回避掉需要付给 SUN 有关 JAVA 的授权费用。 对手机制造者的影响 � Android 是款开源的移动计算...
HelloWorldApp.java 第一个用Java开发的应用程序。 firstApplet.java 第一个用Java开发的Applet小程序。 firstApplet.htm 用来装载Applet的网页文件 第2章 示例描述:本章介绍开发Java的基础语法知识。 ...
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java...
人们熟知的“hello,world”程序就是由本书首次引入的,现在,这一程序已经成为所有程序设计语言入门的第一课。 内容提要 -------------------------------------------------------------------------------- 《C...
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java...
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...
用java进行客户端的applet (小程序)开发的技术已广为使用,而用java进行服务器端的servlet(服务器小程序)开发则尚需揭开其神秘的面纱,本书正是基于这样的目的编写的。全书从java服务器的体系结构、开发工具和...
在 Java 客户端中,我们使用了接口来描述远程方法,之后我们通过 useService 方法返回一个远程代理对象,该对象实现了我们定义的接口,之后我们就可以直接调用远程方法 helloWorld 了。如果你比较细心的话,你还会...
4.1.1 实例:在My Eclipse中编写Helloworld程序 4.1.2 实例:手工编写:Helloworld程序 4.2 Servlet基础 4.2.1 配置数据库连接池 4.2.2 数据库连接池的应用 4.2 -3实例:用doGet方法处理客户端请求 ...
1、实例一:在jni中调用标准c中自带的函数printf(): 57 2、实例二、调用c 语言用户定义的函数... 58 3、实例三、在jni函数中访问java类中的对象实例域... 58 4、实例四:在jni函数中访问类的静态实例域... 60 5...
4.1.1 实例:在My Eclipse中编写Helloworld程序 4.1.2 实例:手工编写:Helloworld程序 4.2 Servlet基础 4.2.1 配置数据库连接池 4.2.2 数据库连接池的应用 4.2 -3实例:用doGet方法处理客户端请求 ...
1、编写一个程序,将a.txt文件中的单词与b.txt文件中的单词交替合并到c.txt文件中,a.txt文件中的单词用回车符分隔,b.txt文件中用回车或空格进行分隔。 61 2、编写一个程序,将d:\java目录下的所有.java文件复制到d...
在 Java 客户端中,我们使用了接口来描述远程方法,之后我们通过 useService 方法返回一个远程代理对象,该对象实现了我们定义的接口,之后我们就可以直接调用远程方法 helloWorld 了。如果你比较细心的话,你还会...