Java提供了native关键字,该关键字用来声明本机代码方法。声明一个本机方法,在该方法之前用native修饰符,但是不要定义任何方法体。例如:
public native int meth() ;
很多本机方法是用C写的。把C代码结合到 Java 程序中的机制是调用。
首先输入java短程序,使用了一个名为square的native方法:
/*
程序名为Main.java
*/
public class Main {
public native int square(int i);
public static void main(String[] args) {
System.loadLibrary("Main");
System.out.println(new Main().square(2));
}
}
注意square(int i )方法声明为native且不含方法体。本地执行方法square( )的动态链接库由loadLibrary( )方法加载。loadLibrary( )方法是System类的组成单元。它的一般形式为:
static void loadLibrary(String filename)
这里,filename是指定保存该库文件名的字符串。在Windows环境下,该文件的扩展名为.DLL;在Linux下,该文件扩展名为.so。
写完程序后,编译它生成Main.class。然后,你必须用javah生成一个文件:Main.h(javah包含在JDK中)。在执行square( )时你要包含Main.h。
为生成Main.h,用下面的命令:
javah -jni Main
生成Main.h文件如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Main */
#ifndef _Included_Main
#define _Included_Main
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_Main_square
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
请特别注意下面一行,该行定义了所要创建的square( )函数的原型:
JNIEXPORT jint JNICALL ;
注意函数的名称是Java_Main_square( )。调用本机函数你必须用这样的名字。也就是说,不是生成一个名为square( )的C函数,而是创建一个名为函数。加入前缀Main是因为它把square( )方法作为Main类的一部分。记住,其他类可以定义它们自己的与Main定义的完全不同的本地square( )方法。前缀中包括类名的方法解决了区分不同版本的问题。作为一个常规方法,给本机函数取名,前缀中必须包括声明它们的类名。
生成了必备的头文件后,可以编写square( )执行文件并把它存在一个名为Main.c的文件中:
#include <jni.h>
#include "Main.h"
JNIEXPORT jint JNICALL Java_Main_square(
JNIEnv *env, jobject obj, jint i) {
return i * i ;
}
上面我们讲到,用javac编译成成.class文件,用javah -jni编译成成.h文件,接下来,我们要编译.c的代码:
gcc -shared -fpic -o libMain.so -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux Main.c
这生成了linux下的动态库so。运行方法:
java -Djava.library.path=. Main
这里需要注意:libMain.so文件名固定,与 System.loadLibrary("Main")中的Main保持一致。命名:Main--->lib+Main+.so--->libMain.so
给出另外一个函数中没有传递变量的例子,实现写好.c文件: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);
new NativeDemo().test2(31);
}
// declare native method
public native void test() ;
public native void test2(int a) ;
// load DLL that contains static method
static {
System.loadLibrary("NativeDemo");
}
}
NavtiveDemo.h文件如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class NativeDemo */
#ifndef _Included_NativeDemo
#define _Included_NativeDemo
#ifdef __cplusplus
extern " C " {
#endif
/*
* Class: NativeDemo
* Method: test
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_NativeDemo_test
(JNIEnv *, jobject);
/*
* Class: NativeDemo
* Method: test2
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_NativeDemo_test2
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
NativeDemo.c文件为:
#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");
}
JNIEXPORT void JNICALL Java_NativeDemo_test2(JNIEnv *env, jobject obj, jint i){
printf("i = %d\n",i);
}
编译的shell文件:
javac NativeDemo.java
javah -jni NativeDemo
gcc -shared -fpic -o libNativeDemo.so -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux NativeDemo.c
java -Djava.library.path=. NativeDemo
rm *~ *class *so