本文目录导读:

我来给您一个完整的JNI(Java Native Interface)案例,从Java调用C/C++代码。
项目结构
JNIDemo/
├── src/
│ └── com/example/
│ └── NativeDemo.java
├── native/
│ ├── native_demo.c
│ └── native_demo.h
└── lib/
步骤1:编写Java类
// NativeDemo.java
package com.example;
public class NativeDemo {
// 声明native方法
public native int add(int a, int b);
public native String greet(String name);
public native int[] sortArray(int[] array);
// 加载动态库
static {
System.loadLibrary("nativedemo");
}
public static void main(String[] args) {
NativeDemo demo = new NativeDemo();
// 测试加法
int sum = demo.add(10, 20);
System.out.println("10 + 20 = " + sum);
// 测试字符串处理
String greeting = demo.greet("小明");
System.out.println("Greeting: " + greeting);
// 测试数组排序
int[] array = {5, 3, 8, 1, 9, 2};
int[] sortedArray = demo.sortArray(array);
System.out.print("Sorted array: ");
for (int num : sortedArray) {
System.out.print(num + " ");
}
System.out.println();
}
}
步骤2:生成C头文件
编译Java文件并生成头文件:
# 编译Java文件 javac src/com/example/NativeDemo.java # 生成头文件 javah -jni -classpath src com.example.NativeDemo
生成的native_demo.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_NativeDemo */
#ifndef _Included_com_example_NativeDemo
#define _Included_com_example_NativeDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_NativeDemo
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_NativeDemo_add
(JNIEnv *, jobject, jint, jint);
/*
* Class: com_example_NativeDemo
* Method: greet
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_NativeDemo_greet
(JNIEnv *, jobject, jstring);
/*
* Class: com_example_NativeDemo
* Method: sortArray
* Signature: ([I)[I
*/
JNIEXPORT jintArray JNICALL Java_com_example_NativeDemo_sortArray
(JNIEnv *, jobject, jintArray);
#ifdef __cplusplus
}
#endif
#endif
步骤3:实现C代码
// native_demo.c
#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "native_demo.h"
// 实现加法
JNIEXPORT jint JNICALL Java_com_example_NativeDemo_add
(JNIEnv *env, jobject obj, jint a, jint b) {
printf("C: Adding %d + %d\n", a, b);
return a + b;
}
// 实现字符串处理
JNIEXPORT jstring JNICALL Java_com_example_NativeDemo_greet
(JNIEnv *env, jobject obj, jstring name) {
// 将Java字符串转换为C字符串
const char *nativeName = (*env)->GetStringUTFChars(env, name, NULL);
if (nativeName == NULL) {
return NULL; // 内存分配失败
}
printf("C: Greeting %s\n", nativeName);
// 创建新的字符串
char greeting[100];
snprintf(greeting, sizeof(greeting), "你好,%s!来自C的问候!", nativeName);
// 释放原始字符串
(*env)->ReleaseStringUTFChars(env, name, nativeName);
// 将C字符串转换为Java字符串并返回
return (*env)->NewStringUTF(env, greeting);
}
// 实现数组排序
JNIEXPORT jintArray JNICALL Java_com_example_NativeDemo_sortArray
(JNIEnv *env, jobject obj, jintArray array) {
// 获取数组长度
jsize length = (*env)->GetArrayLength(env, array);
// 获取数组元素
jint *elements = (*env)->GetIntArrayElements(env, array, NULL);
if (elements == NULL) {
return NULL; // 内存分配失败
}
printf("C: Sorting array of length %d\n", length);
// 打印原始数组
printf("Original array: ");
for (int i = 0; i < length; i++) {
printf("%d ", elements[i]);
}
printf("\n");
// 使用冒泡排序
for (int i = 0; i < length - 1; i++) {
for (int j = 0; j < length - 1 - i; j++) {
if (elements[j] > elements[j + 1]) {
int temp = elements[j];
elements[j] = elements[j + 1];
elements[j + 1] = temp;
}
}
}
// 创建新的Java数组
jintArray result = (*env)->NewIntArray(env, length);
if (result == NULL) {
(*env)->ReleaseIntArrayElements(env, array, elements, 0);
return NULL;
}
// 将排序后的数据设置到新数组
(*env)->SetIntArrayRegion(env, result, 0, length, elements);
// 释放原始数组
(*env)->ReleaseIntArrayElements(env, array, elements, 0);
return result;
}
步骤4:编译动态库
Linux/Mac系统:
# 编译C代码为动态库
gcc -shared -fPIC -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \
-o lib/libnativedemo.so native/native_demo.c
Windows系统:
# 使用MinGW或Visual Studio
gcc -shared -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" \
-o lib\nativedemo.dll native\native_demo.c
Mac OS X系统:
gcc -shared -fPIC -I$JAVA_HOME/include -I$JAVA_HOME/include/darwin \
-o lib/libnativedemo.dylib native/native_demo.c
步骤5:运行程序
# 指定库路径并运行 java -Djava.library.path=lib -classpath src com.example.NativeDemo
完整构建脚本(build.sh)
#!/bin/bash
# 设置JAVA_HOME(根据自己的环境修改)
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
# 清理
rm -rf src/com/example/*.class
rm -rf lib/*.so lib/*.dylib
# 编译Java
echo "Compiling Java..."
javac src/com/example/NativeDemo.java
# 生成头文件
echo "Generating header file..."
javah -jni -classpath src com.example.NativeDemo
mv com_example_NativeDemo.h native/
# 编译C动态库
echo "Compiling C library..."
gcc -shared -fPIC -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \
-o lib/libnativedemo.so native/native_demo.c
echo "Build complete!"
# 运行测试
echo "Running test..."
java -Djava.library.path=lib -classpath src com.example.NativeDemo
输出结果
C: Adding 10 + 20
10 + 20 = 30
C: Greeting 小明
Greeting: 你好,小明!来自C的问候!
C: Sorting array of length 6
Original array: 5 3 8 1 9 2
Sorted array: 1 2 3 5 8 9
关键概念解释
-
JNIEnv:代表JNI环境的指针,用于调用JNI函数
-
jobject:表示Java对象的引用
-
类型映射:
jint↔ Javaintjstring↔ JavaStringjintArray↔ Javaint[]
-
JNI函数:
GetStringUTFChars():获取Java字符串的C表示NewStringUTF():创建Java字符串GetIntArrayElements():获取Java数组的C表示ReleaseStringUTFChars():释放字符串资源
这个案例涵盖了JNI中最常见的操作:基本类型传递、字符串处理和数组操作。