Java中调用C代码的方法与实践
在Java开发中,有时需要调用C/C++编写的代码,以实现高性能计算、访问底层硬件资源或复用现有C库的功能,Java通过JNI(Java Native Interface)提供了与本地代码交互的能力,本文将详细介绍Java调用C代码的实现方法、步骤及注意事项。

JNI的基本原理
JNI是Java平台的一部分,它允许Java代码与用其他语言(如C/C++)编写的应用程序和库进行交互,其核心思想是通过本地方法接口,将Java代码与本地代码连接起来,具体流程如下:
- 声明本地方法:在Java类中使用
native关键字声明需要调用的本地方法。 - 生成头文件:使用
javac编译Java代码,再通过javah(或JDK 9+的javac -h)生成C头文件。 - 编写本地方法实现:根据生成的头文件,用C/C++实现本地方法。
- 编译生成动态链接库:将C/C++代码编译为平台特定的动态链接库(如Windows的
.dll、Linux的.so)。 - 加载并调用:在Java代码中加载动态链接库,并调用本地方法。
实现步骤详解
声明本地方法
在Java类中声明一个或多个native方法。
public class NativeExample {
// 声明一个本地方法,接收两个int参数,返回int类型结果
public native int add(int a, int b);
static {
// 加载动态链接库(无需扩展名)
System.loadLibrary("NativeExample");
}
public static void main(String[] args) {
NativeExample example = new NativeExample();
int result = example.add(10, 20);
System.out.println("Result: " + result);
}
}
生成C头文件
编译Java代码后,使用javac生成.class文件,再通过以下命令生成头文件:
javac NativeExample.java javac -h . NativeExample.java # JDK 9+ 推荐方式
执行后会生成NativeExample.h类似:
#include <jni.h>
#ifndef _Included_NativeExample
#define _Included_NativeExample
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_NativeExample_add(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
编写C实现代码
根据生成的头文件,编写C代码实现本地方法。
#include "NativeExample.h"
#include <stdio.h>
JNIEXPORT jint JNICALL Java_NativeExample_add(JNIEnv *env, jobject obj, jint a, jint b) {
printf("C: Called add function with %d and %d\n", a, b);
return a + b;
}
编译生成动态链接库
根据不同操作系统,使用编译器生成动态链接库:

- Windows(MinGW):
gcc -shared -IC:\Java\jdk-11\include -IC:\Java\jdk-11\include\win32 -o NativeExample.dll NativeExample.c
- Linux(GCC):
gcc -shared -I/usr/lib/jvm/java-11-openjdk-amd64/include -o libNativeExample.so NativeExample.c
运行Java程序
将动态链接库放在Java库路径(java.library.path)中,运行程序:
java -Djava.library.path=. NativeExample
输出结果:
C: Called add function with 10 and 20
Result: 30
JNI中的数据类型映射
JNI定义了Java与C/C++数据类型的映射关系,常见类型如下:
| Java类型 | JNI类型 | C/C++类型 |
|———-|———|——————-|
| int | jint | int |
| long | jlong | long long |
| double | jdouble| double |
| String | jstring| const jchar* |
| Object | jobject| void* |
对于复杂类型(如字符串、数组),需要通过JNI函数进行转换,处理String:
JNIEXPORT void JNICALL Java_NativeExample_printString(JNIEnv *env, jobject obj, jstring str) {
const char *cStr = (*env)->GetStringUTFChars(env, str, NULL);
printf("C: Received string: %s\n", cStr);
(*env)->ReleaseStringUTFChars(env, str, cStr);
}
高级特性与注意事项
异常处理
本地代码中需要手动检查和处理Java异常:
jthrowable exc = (*env)->ExceptionOccurred(env);
if (exc) {
(*env)->ExceptionDescribe(env); // 打印异常信息
(*env)->ExceptionClear(env); // 清除异常
}
内存管理
JNI提供了内存管理函数,如NewGlobalRef和DeleteGlobalRef,用于避免内存泄漏。

性能优化
- 避免频繁的本地方法调用,尽量减少Java与C/C++之间的数据传递。
- 使用
DirectByteBuffer处理大块数据,减少内存拷贝。
跨平台兼容性
动态链接库的命名和路径因平台而异,需注意:
- Windows:
NativeExample.dll - Linux/macOS:
libNativeExample.so
替代方案
除了JNI,还有其他方式实现Java与C/C++交互:
-
JNA(Java Native Access):通过简化接口调用本地库,无需编写C代码。
import com.sun.jna.Library; import com.sun.jna.Native; public interface CLibrary extends Library { int add(int a, int b); } public class JnaExample { public static void main(String[] args) { CLibrary lib = Native.load("msvcrt", CLibrary.class); int result = lib.add(10, 20); System.out.println("Result: " + result); } } -
JavaCPP:提供更高级的封装,支持C++模板和STL。
JNI是Java调用C代码的传统方式,功能强大但使用复杂,开发者需注意数据类型映射、内存管理和异常处理,对于简单场景,JNA等替代方案更为便捷,根据实际需求选择合适的技术,可以高效实现Java与本地代码的交互。



















