Web 端人臉識別項目:核心算法用 C++ 實現,Java 後台需通過 JNI (Java Native Interface) 調用 DLL 庫實現高性能計算。
package com.test.jni;
public class Java2cpp {
// 載入 DLL (文件名不含擴展名)
static { System.loadLibrary("JavaCallcpp"); }
// 聲明 native 方法
public native int DLL_ADD(int a, int b);
public native int DLL_SUB(int a, int b);
public native int DLL_MUL(int a, int b);
public native int DLL_DIV(int a, int b);
public static void main(String[] args) {
Java2cpp jni = new Java2cpp();
System.out.println("加法: " + jni.DLL_ADD(10, 5));
System.out.println("減法: " + jni.DLL_SUB(10, 5));
System.out.println("乘法: " + jni.DLL_MUL(10, 5));
System.out.println("除法: " + jni.DLL_DIV(10, 5));
}
}
# 1. 編譯 Java 文件 javac com/test/jni/Java2cpp.java # 2. 生成頭文件 (在 bin 目錄下執行) javah -classpath . -jni com.test.jni.Java2cpp # 生成: com_test_jni_Java2cpp.h
生成的頭文件關鍵內容:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include#ifndef _Included_com_test_jni_Java2cpp #define _Included_com_test_jni_Java2cpp #ifdef __cplusplus extern "C" { #endif JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_DLL_1ADD (JNIEnv *, jobject, jint, jint); JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_DLL_1SUB (JNIEnv *, jobject, jint, jint); JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_DLL_1MUL (JNIEnv *, jobject, jint, jint); JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_DLL_1DIV (JNIEnv *, jobject, jint, jint); #ifdef __cplusplus } #endif #endif
TestJNIcom_test_jni_Java2cpp.h 到項目目錄JDK安裝路徑/includeJDK安裝路徑/include/win32dllApi.h)#include "com_test_jni_Java2cpp.h" int DLL_API_ADD(int a, int b); int DLL_API_SUB(int a, int b); int DLL_API_MUL(int a, int b); int DLL_API_DIV(int a, int b);
dllApi.cpp)#include "stdafx.h"
#include "dllApi.h"
int DLL_API_ADD(int a, int b) { return a + b; }
int DLL_API_SUB(int a, int b) { return a - b; }
int DLL_API_MUL(int a, int b) { return a * b; }
int DLL_API_DIV(int a, int b) { return a / b; }
TestJNI.cpp)#include "stdafx.h"
#include "TestJNI.h"
#include "com_test_jni_Java2cpp.h"
#include "dllApi.h"
JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_DLL_1ADD
(JNIEnv *env, jobject obj, jint a, jint b) {
return DLL_API_ADD(a, b);
}
JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_DLL_1SUB
(JNIEnv *env, jobject obj, jint a, jint b) {
return DLL_API_SUB(a, b);
}
JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_DLL_1MUL
(JNIEnv *env, jobject obj, jint a, jint b) {
return DLL_API_MUL(a, b);
}
JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_DLL_1DIV
(JNIEnv *env, jobject obj, jint a, jint b) {
return DLL_API_DIV(a, b);
}
命名規則:Java_包名_類名_方法名,方法名中 _1 代表下劃線轉義。
TestJNI.dll (位於 Debug/ 或 Release/)TestJNI.dll → JavaCallcpp.dll (與 System.loadLibrary 參數一致)JavaCallcpp.dll 到 JRE/bin/ 或項目啟動目錄,或配置 java.library.pathjava com.test.jni.Java2cpp # 輸出: # 加法: 15 # 減法: 5 # 乘法: 50 # 除法: 2
| Java 類型 | JNI 類型 | C/C++ 類型 |
|---|---|---|
| boolean | jboolean | unsigned char (8位) |
| byte | jbyte | signed char (8位) |
| char | jchar | unsigned short (16位) |
| short | jshort | short (16位) |
| int | jint | int (32位) |
| long | jlong | long long (64位) |
| float | jfloat | float (32位) |
| double | jdouble | double (64位) |
| String | jstring | 需用 GetStringUTFChars 轉換 |
| int[] | jintArray | 需用 GetIntArrayElements 訪問 |
| Object | jobject | 通用對象引用 |
// Java
public native String processString(String input);
// C++
JNIEXPORT jstring JNICALL Java_com_test_jni_Java2cpp_processString
(JNIEnv *env, jobject obj, jstring input) {
const char *str = env->GetStringUTFChars(input, NULL);
// 處理邏輯...
char result[256];
sprintf(result, "Processed: %s", str);
env->ReleaseStringUTFChars(input, str);
return env->NewStringUTF(result);
}
// Java
public native int sumArray(int[] arr);
// C++
JNIEXPORT jint JNICALL Java_com_test_jni_Java2cpp_sumArray
(JNIEnv *env, jobject obj, jintArray arr) {
jint *elements = env->GetIntArrayElements(arr, NULL);
jsize len = env->GetArrayLength(arr);
int sum = 0;
for (int i = 0; i < len; i++) sum += elements[i];
env->ReleaseIntArrayElements(arr, elements, 0); // 0: 複製回並釋放
return sum;
}
env->ExceptionCheck()GetXXX 必須配對 ReleaseXXX,防止內存洩漏NewGlobalRef,用畢 DeleteGlobalRefAttachCurrentThread| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| JNI | 性能最高、直接調用、無序列化開銷 | 開發複雜、平台相關、崩潰風險高、調試困難 | 高性能計算、硬件交互、legacy C++庫復用 |
| JNA (Java Native Access) | 純 Java、無需編譯 C++、跨平台、開發簡單 | 性能較 JNI 低 10-20%、複雜結構體映射麻煩 | 快速集成、簡單接口、跨平台需求 |
| 進程間通信 (Socket/gRPC) | 解耦合、語言無關、獨立部署、故障隔離 | 網絡延遲、序列化開銷、運維複雜度高 | 微服務架構、重型計算服務、多語言團隊 |
JNI 是 Java 與 C/C++ 互操作的橋樑,核心流程:Java 聲明 native → javah 生成頭文件 → C++ 實現 JNIEXPORT → 編譯 DLL → System.loadLibrary 載入。關鍵點在於命名規範匹配、數據類型正確映射、內存管理嚴謹。生產環境建議評估 JNA 或微服務化替代方案,僅在極致性能場景堅持使用 JNI。