JNI (Java Native Interface) 打破 Java 跨平台封閉性,實現 Java 與 C/C++ 代碼互調。典型場景:
package com.example.jni;
public class NativeCalculator {
// 加載動態庫:Windows -> calculator.dll, Linux -> libcalculator.so
static {
System.loadLibrary("calculator");
}
// native 關鍵字聲明,無方法體
public native int add(int a, int b);
public native double sqrt(double x);
public static void main(String[] args) {
NativeCalculator calc = new NativeCalculator();
System.out.println("5 + 3 = " + calc.add(5, 3));
System.out.println("sqrt(16) = " + calc.sqrt(16));
}
}
javac com/example/jni/NativeCalculator.java
# 進入 src 根目錄執行
javah -classpath . -jni com.example.jni.NativeCalculator
生成 com_example_jni_NativeCalculator.h:
/* DO NOT EDIT - machine generated */
#include
#ifndef _Included_com_example_jni_NativeCalculator
#define _Included_com_example_jni_NativeCalculator
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_com_example_jni_NativeCalculator_add
(JNIEnv *, jobject, jint, jint);
JNIEXPORT jdouble JNICALL Java_com_example_jni_NativeCalculator_sqrt
(JNIEnv *, jobject, jdouble);
#ifdef __cplusplus
}
#endif
#endif
#include "com_example_jni_NativeCalculator.h"
#include
JNIEXPORT jint JNICALL Java_com_example_jni_NativeCalculator_add
(JNIEnv *env, jobject obj, jint a, jint b) {
return a + b;
}
JNIEXPORT jdouble JNICALL Java_com_example_jni_NativeCalculator_sqrt
(JNIEnv *env, jobject obj, jdouble x) {
return sqrt(x);
}
關鍵點:
Java_包名_類名_方法名(點替換為下劃線)JNIEnv*:JNI 環境指針,提供數組/字符串/對象操作 APIjobject:調用對象實例(非靜態方法),靜態方法為 jclassjint→int、jdouble→double、jstring→String 等Linux/macOS (GCC):
gcc -shared -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux \
-o libcalculator.so com_example_jni_NativeCalculator.c
Windows (MSVC):
cl /I"%JAVA_HOME%\include" /I"%JAVA_HOME%\include\win32" /LD \
com_example_jni_NativeCalculator.c /Fe:calculator.dll
# Linux/macOS
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
java com.example.jni.NativeCalculator
# Windows
set PATH=.;%PATH%
java com.example.jni.NativeCalculator
5 + 3 = 8
sqrt(16) = 4.0
// Java 聲明
public native int sumArray(int[] arr);
public native String reverseString(String str);
// C 實現
JNIEXPORT jint JNICALL Java_com_example_jni_NativeCalculator_sumArray
(JNIEnv *env, jobject obj, jintArray arr) {
jint *elements = (*env)->GetIntArrayElements(env, arr, NULL);
jsize len = (*env)->GetArrayLength(env, arr);
int sum = 0;
for (int i = 0; i < len; i++) sum += elements[i];
(*env)->ReleaseIntArrayElements(env, arr, elements, 0); // 釋放
return sum;
}
JNIEXPORT jstring JNICALL Java_com_example_jni_NativeCalculator_reverseString
(JNIEnv *env, jobject obj, jstring str) {
const char *cstr = (*env)->GetStringUTFChars(env, str, NULL);
// 反轉邏輯...
(*env)->ReleaseStringUTFChars(env, str, cstr);
return (*env)->NewStringUTF(env, reversed);
}
Get*ArrayElements/GetStringUTFChars 必須配對 Release*ThrowNew,不可直接拋 C++ 異常JNIEnv 線程私有,不可跨線程傳遞;需 AttachCurrentThread 附加線程jclass/jmethodID/jfieldID,避免重複查找