Java中native关键字的作用与JNI调用C/C++实战教程
在Java开发中,当需要与操作系统底层交互或调用高性能本地代码时,native关键字是不可或缺的桥梁。本文将详细解释其作用,并结合JDK源码实例,手把手教你使用JNI标准在Windows环境下通过C语言编写并加载本地动态库。
1、理解native关键字与JNI基础
native是Java中的一个修饰符,用于声明一个方法由非Java语言(通常是C或C++)实现。在JDK的Object.java等核心类中,许多关键方法如getClass()、hashCode()和clone()均被标记为native:
public final native Class<?> getClass(); public native int hashCode(); protected native Object clone() throws CloneNotSupportedException;
为什么需要JNI?
Java虚拟机(JVM)基于Native Interface运行,而JNI(Java Native Interface,Java本地接口)正是允许Java代码与其他语言编写的代码交互的标准。主要应用场景包括:
- ①、访问平台特定的功能,例如直接操作硬件或与操作系统底层进行深度交互。
- ②、复用已有且成熟的本地代码库(Legacy Libraries),避免在Java中重新实现。
- ③、对性能要求极高的代码段(如算法计算密集型任务),通过使用接近硬件的语言(如汇编或C/C++)来提升执行效率。
JNI的设计初衷是为了支持C和C++,但其调用约定也兼容其他语言。虽然使用native会打破Java“一次编写,到处运行”的跨平台特性(因为需要在不同系统下重新编译本地库),但在特定需求下这种取舍是必要且值得的。
JNI的主要缺点
在使用JNI时需注意以下风险:程序不再跨平台。例如在Windows上生成的.dll文件不能在Linux或macOS上直接运行。此外,本地代码的不当处理可能导致整个JVM进程崩溃。为了降低风险,建议尽量将本地方法的调用集中在少数几个类中进行封装,以减少耦合度。
2、实战:用C语言编写并调用本地方法
接下来,我们将通过一个完整的“Hello World”案例,展示如何在Windows环境下利用命令行动态编译C语言代码并生成Java可运行的动态库。
所有操作步骤均在目录D:\JNI下完成。
第1步:编写带有native声明的Java类
首先,创建一个Java类,声明该方法需要使用native,并在静态代码块中使用System.loadLibrary()加载对应名称的动态库。
public class HelloJNI {
// 告知JVM该方法在外部定义
private native void helloJNI();
static {
System.loadLibrary("helloJNI"); // 加载动态库 helloJNI.dll/helloJNI.so
}
public static void main(String[] args) {
HelloJNI jni = new HelloJNI();
jni.helloJNI();
}
}注意:如果此时直接运行,由于缺少本地实现,将抛出A Java Exception has occurred错误。该错误提示表明JVM找不到预期的原生符号。
第2步:编译Java类生成.class文件
使用JDK自带的javac编译器编译源文件:
javac HelloJNI.java
执行成功后,当前目录下将生成HelloJNI.class文件。
第3步:生成C语言的H头文件
为了让C/C++知道我们要实现哪些Java方法,需要使用JDK提供的javah工具生成对应的C语言头文件。
javah -jni HelloJNI
执行后,会在D:\JNI目录下自动生成HelloJNI.h文件。我们可以打开它查看方法签名:
第4步:编写C语言实现并编译成DLL
如果系统中没有安装Visual Studio,我们需要配置C语言编译器(如MinGW/gcc)。确保系统能识别g++指令。
新建一个名为HelloJNI.c的文件,实现头文件中导出的函数:
#include "HelloJNI.h"
#include <jni.h>
#include <stdio.h>
JNIEXPORT void JNICALL Java_HelloJNI_helloJNI(JNIEnv *env, jobject obj) {
printf("Hello JNI!\n");
return;
}接着,使用gcc命令编译C文件为Windows平台的动态链接库(.dll)。编译时需要指定JDK的包含路径以及生成64位动态库的参数:
gcc -m64 -Wl,--add-stdcall-alias -I"C:\Program Files\Java\jdk1.8.0_152\include" -I"C:\Program Files\Java\jdk1.8.0_152\include\win32" -shared -o helloJNI.dll HelloJNI.c
执行完成后,你会在当前文件夹中找到新生成的helloJNI.dll文件:
第5步:运行并验证结果
回到Java环境,再次运行HelloJNI.java:
java HelloJNI
控制台成功输出Hello JNI!,标志着C与Java之间的本地调用链路打通。
3、JNI与C交互的整体流程图
总结上述步骤,一个标准的Native Method交互流程可以概括为:
4、关于native语法的规范
除了通过JNI调用,了解native本身的语法特征也有助于理解其设计意图:
- ①、修饰位置:必须在返回类型之前。
- ②、不可抽象:
native修饰的方法不能同时使用abstract修饰,且不能包含方法体(即没有大括号)。 - ③、返回值类型:可以是任意Java数据类型。
在日常阅读开源框架(如JDK核心包)代码时,只要看到native方法,便知该方法的具体逻辑是由底层的操作系统或JVM内部实现的,我们只需关注其在Java层面的功能表现即可。
总结
以上就是Java中native关键字的详细解析及JNI结合C语言实现本地调用的详细内容,更多关于Java性能优化与跨语言编程的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









