Java中的Native方法:完整攻略
理解Native方法
JNI(Java Native Interface)可以让Java应用程序在运行时,与C/C++语言编写的函数进行交互。因为Java虚拟机(JVM)不能直接运行非Java代码, JNI支持调用Native方法,Native方法是一些用其他编程语言(如C/C++)编写的方法。
Native方法是指C/C++(通常是C)实现的公共本地方法接口(C即JNI规范定义的C规范)。在涉及到如硬件驱动、GUI编程,以及涉及到大量计算的情况时,使用Java编写的代码可能不够快、不够优化、不够灵活,此时可以考虑使用Native方法,以获得更好的性能表现。
Native方法的定义有如下格式:
public native int foo(int a, int b);
在C/C++的代码中,需要声明foo方法的共有本地方法接口:
JNIEXPORT jint JNICALL Java_FooClass_foo(JNIEnv *env, jobject obj, jint a, jint b) {...}
其中,JNIEXPORT和JNICALL是宏定义,分别用于告知C/C++编译器导出的函数符合Jave native接口规范。Java_FooClass_foo即为Java代码中定义的native方法的标识符:Java类名_foo方法名。
Native方法的定义
在Java中定义Native方法具体步骤:
- 声明native方法,如:
java
public native void myFunction(int x);
-
编译Java源文件生成类文件(.class)。
-
生成头文件,执行如下命令:
bash
javah com.mycompany.mylibrary.MyClass
其中,com.mycompany.mylibrary.MyClass为含有native函数的类的全限定名。
- 自动生成的.h文件中声明了native方法的原型,如:
c
JNIEXPORT void JNICALL Java_com_mycompany_mylibrary_MyClass_myFunction(JNIEnv *, jobject, jint);
- 编写实现native方法的C/C++代码。
c
JNIEXPORT void JNICALL Java_com_mycompany_mylibrary_MyClass_myFunction(JNIEnv *env, jobject obj, jint x) {
...
}
- 将Native方法所编写的C源文件编译成动态链接库(DLL或so文件)。
以Linux为例,执行如下命令:
```bash
gcc -c -fPIC xx.c -o xx.o #将源程序编译成目标代码
gcc -shared -Wl,-soname,xx.so.xx.o -o xx.so #将目标代码编译链接成动态库文件
```
- Java调用Native方法。
示例1:字符串拼接
下面这个例子展示了Native方法与Java方法直接的性能对比:
// Java代码
public class StringConcatenationExample {
public static void main(String[] args) {
int n = 10000000;
String s = "";
long start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
s += "hello world";
}
long end = System.currentTimeMillis();
System.out.println("Java String concatenation took: " + (end - start) + "ms");
}
}
// Native C代码
#include <jni.h>
#include <string.h>
JNIEXPORT jstring JNICALL Java_StringConcatenationExample_concatenate(JNIEnv *env, jobject obj, jint n) {
char *str = "hello world";
size_t len = strlen(str);
char *data = malloc(len * n + 1);
memset(data, 0, len * n + 1);
for (int i = 0; i < n; i++) {
memcpy(data + i * len, str, len);
}
jstring ret = (*env)->NewStringUTF(env, data);
free(data);
return ret;
}
测试结果显示,循环10,000,000次字符串拼接的平均 Java 字符串拼接耗时为 3944 ms,而使用 Native C方法的字符串拼接耗时仅为 167 ms。
示例2:Java与C++通信
下面这个例子展示了Java调用C++ Native方法的基本实现:
//Java代码
public class HelloWorldJNI {
static {
System.loadLibrary("Hello");
}
private native void greeting();
public static void main(String[] args) {
new HelloWorldJNI().greeting();
}
}
// Native C++代码
#include <jni.h>
#include <iostream>
JNIEXPORT void JNICALL Java_HelloWorldJNI_greeting(JNIEnv *env, jobject obj) {
std::cout << "Hello World from C++!" << std::endl;
}
在上述代码中,native关键字用于告诉Java虚拟机,该方法是一个Native方法。greeting()方法是void类型,因此对应的C++实现函数也是void类型。greeting()方法使用System.loadLibrary()加载Native库Hello,这个库是使用C++编写的,库的名字后缀为.so或.dll(根据平台)。
我们先使用Java工具javac编译Java代码:
javac HelloWorldJNI.java
然后使用Java工具javah编译Class生成头文件。这个命令语句将在当前工作目录中生成HelloWorldJNI.h头文件:
javah HelloWorldJNI
然后,我们实现生成的本机方法。创建一个名为Hello.cpp的文件(文件名取决于您的选择,只需确保到.class文件的路径和名称与Hello.cpp中的名称匹配即可),使用g++ (Linux)或cl.exe/VS(Windows)编译:
g++ -I"/path/to/Java/include/" -I"/path/to/Java/include/linux/" -shared -o libHello.so HelloWorldJNI.cpp -fPIC
或者,在 Windows 上,使用 Visual Studio:
cl /I"[path\to\Java\include]" /I"[path\to\Java\include\win32]" /LD HelloWorldJNI.cpp /FeHello.dll
最后,启动Java程序:
java HelloWorldJNI
运行时输出如下语句:
Hello World from C++!
到此,我们就展示了为Java编写Native方法的完整攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中的Native方法 - Python技术站