首页 > 代码库 > Java native方法、JNI实例及常见错误分析
Java native方法、JNI实例及常见错误分析
1.概述
今天在看java关于调用本机代码子程序来获得较快的执行时间,或者,你希望用一个专用的第三方的库,例如统计学包。然而,因为Java程序被编译为字节码,字节码有Java运行时系统解释(或动态编译),看起来在Java程序中调用本机代码子程序是不可能。幸运的是,这个结论是错误的。Java提供了native关键字,该关键字用来声明本机代码方法。一旦声明,这些方法可以在Java程序中被调用,就像调用其他Java方法一样。
2.native关键字用法
既然Java提供了native方法,那么如何实现呢?native是与C++联合开发的时候用的!使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由Java调用。这些函数的实现体在DLL中,JDK的源码中并不包含,你应该是看不到的。对于不同的平台它们也是不同的。这也是java的底层机制,实际上java就是在不同的平台上调用不同的native方法实现对操作系统的访问的。总而言之:
- native是用做java和其他语言(如C++)进行协作时使用,也就是native后的函数的实现不是用java写的。
- 既然都不是java,那就别管它的源代码了,我们只需要知道这个方法已经被实现即可。
- native的意思就是通知操作系统,这个函数你必须给我实现,因为我要使用。所以native关键字的函数都是操作系统实现的,java只能调用。
- java是跨平台的语言,既然是跨平台,所付出的代价就是牺牲对底层的控制,而java要实现对底层的控制,就需要一些语言的帮助,这个就是native的作用了。
3.JNI简介
native方法是通过java中的JNI实现的。JNI是Java Native Interface的缩写。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受到支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java虚拟机实现下。
目前java与dll交互的技术主要有三种:jni、jawin和jacob。JNI(Java Native Interface)是sun提供的java与系统中的原生方法交互的技术(在windows\linux系统中,实现java与native method互调)。目前只能由C/C++实现。后两个都是sourceforge上的开源项目,同时也都是基于JNI技术的windows系统上的应用库。Jacob(Java-ComBridge)提供了java程序调用microsoft的com对象中的方法的能力。而除了com对象外,jawin(Java/Win32 integration project)还可以win32-dll动态链接库中的方法。就功能而言:JIN>>jawin>jacob.就易用性而言,正好相反:jacob>jawin>>JNI
JVM封装了各种操作系统实际的差异性的同时,提供了JNI技术,使得开发者可以通过java程序(代码)调用到操作系统相关的技术实现的函数,从而与其他技术和系统交互,使用其他技术实现的功能;同时其他技术和系统也可以通过jni提供的相应原生接口调用java应用系统内部实现的功能。
在windows系统上,一般可执行的应用程序都是基于native的PE结构,windows上的jvm也是基于native结构实现的。Java应用体系都是构建与jvm之上。
JNI对于应用本身来说,可以看做一个代理模式。对于开发者来说,需要使用C/C++来实现一个的代理程序(jni程序)来实际操作目标原生函数,java程序中则是JVM通过加载并调用此JNI程序来间接地调用目标原生函数。
4.JNI的书写步骤
- 编写带有native声明的方法的java类,生成javawen文件
- 使用javac命令编译所编写的java类,生成.class文件
- 使用javah -jni java类名生成扩展名为h的头文件,也即为生成.h文件。
- 使用C/C++(或者其他编程语言)实现本地方法,创建.h文件的实现,也就是创建.cpp文件实现.h文件实现的.h文件中的方法。
- 将C/C++编写的文件生成动态链接库,生成dll文件
5.JNI实例
下列是所有操作都在目录:D:\Native下进行的,这样做的好处是便于控制。还有另外一个要求是我们的java类不含包名。不然在使用javah -jni class文件名会提示错误:找不到‘class’的类文件。
- 编写带有native声明的方法的java类:NativeDemo.java.
public class NativeDemo { int i; public static void main(String[] args) { NativeDemo ob=new NativeDemo(); ob.i=10; System.out.println("This is ob.i before the native method:"+ ob.i); ob.test();//call a native method. System.out.println("This is ob.i after the native method:"+ ob.i); } //declare native method public native void test(); //load DLL that contains static method static{ System.loadLibrary("NativeDemo"); } }
在编写的Java类时候,一定要注意不要带包名。尤其用eclipse时候自动添加包名。
- 使用javac命令编译所编写的java类:D:\Native>javac NativeDemo.java。执行完上述命令以后生成D:\Native\NativeDemo.class
- 使用javah -jni java类名生成扩展名为h的头文件:D:\Native>javah -jni NativeDemo。执行完上述命令以后生成D:\Native\NativeDemo.h文件,该文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class NativeDemo */#ifndef _Included_NativeDemo#define _Included_NativeDemo#ifdef __cplusplusextern "C" {#endif/* * Class: NativeDemo * Method: test * Signature: ()V */JNIEXPORT void JNICALL Java_NativeDemo_test (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif
这个h文件相当于我们在java里面的接口,这里声明了一个Java_NativeDemo_test(JNIEnv *, jobject).方法,然后在我们的 本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里一致。
- 使用C/C++实现本地方法:创建NativeDemo.c,代码如下所示:
*This file contains the C version of the test() method*/#include<jni.h>#include "NativeDemo.h"#include <stdio.h>JNIEXPORT void JNICALL Java_NativeDemo_test(JNIEnv *env,jobject obj){ jclass cls; jfieldID fid; jint i; printf("Starting the native method.\n"); cls=(*env)->GetObjectClass(env, obj); fid=(*env)->GetFieldID(env, cls, "i","I"); if(fid==0){ printf("Could not get field id.\n"); return; } i=(*env)->GetIntField(env ,obj,fid); printf("i=%d\n",i); (*env)->SetIntField(env,obj,fid,2*i); printf("Ending the native method.\n"); }
- 将C/C++编写的文件生成动态连接库:将D:\Program Files\Java\jdk1.7.0_07\include\jni.h和D:\Program Files\Java\jdk1.7.0_07\include\win32\jni_md.h这个两个文件拷贝到D:\Native\目录下。
- 执行cl/LD: D:\Native\NativeDemo.c得到NativeDemo.dll文件。这里要用到visual studio 2010,要使用其中的cl命令,必须打开visual studio命令行,如下图所示:
如果你电脑是32操作系统,就选用红色方框进行编译,如果64位操作系统选择红色方框下面那个进行编译。具体操作如下:
执行完上述命令后,在D:\Program Files\Microsoft Visual Studio 10.0\VC可以看到生成的四个文件,分别是:
- NativeDemo.dll
- NativeDemo.exp
- NativeDemo.lib
- NativeDemo.obj
将其中的NativeDemo.dll拷贝到D:\Native\目录下。
7.执行class得到结果
在cmd中运行:在D:\Native\目录下:java NativeDemo 。运行结果如图所示。
7.注意事项:
- java源文件不要带有包名。我测试没有包名可以顺利生成h文件。java源文件带有包名在生成h文件时候,提示错误找不到“class类名”的类文件。
- 在将jni.h和jni_md.h拷贝到D:\Native\目录下,编译时候出现错误:fatal error C1083:Cannot open include file:‘jni.h‘。这时候解决办法是:将D:\Program Files\Java\jdk1.7.0_07\include\jni.h、D:\Program Files\Java\jdk1.7.0_07\include\win32\jawt_md.h、D:\Program Files\Java\jdk1.7.0_07\include\win32\jni_md.h拷贝到D:\Program Files\Microsoft Visual Studio 10.0\VC\include\目录下就可以完美解决这个问题。
- 在使用visual studio 2010命令提示符时候选择相对应的32/64操作系统下进行编译。否则会出现报错。
Java native方法、JNI实例及常见错误分析