JNI 讓 Java 程式能調用 C/C++ 編寫的本地庫,解決效能敏感、硬體控制、legacy 代碼複用等場景。本文以「字串拼接」為例,完整演示從 Java 類定義到 C 實現、編譯生成動態庫、運行驗證的全流程。
public class Test {
// 宣告 native 方法
public native String helloworld(String text);
public static void main(String[] args) {
// 載入動態庫:Windows 對應 Test2.dll,Linux 對應 Test2.so
System.loadLibrary("Test2");
Test ts = new Test();
String result = ts.helloworld("world");
System.out.println(result); // 預期輸出:helloworld
}
}
javac Test.java
javah Test
生成 Test.h,關鍵函數簽名:
JNIEXPORT jstring JNICALL Java_Test_helloworld (JNIEnv *, jobject, jstring);
JNIEXPORT:導出符號jstring:返回 Java String 對應類型JNICALL:呼叫慣例Java_Test_helloworld:固定命名規則 Java_全類名_方法名JNIEnv* (JNI 環境指標)、jobject (this 參照)、jstring (Java 字串參數)#include "Test.h"
#include <string.h>
JNIEXPORT jstring JNICALL Java_Test_helloworld (JNIEnv *env, jobject obj, jstring string) {
// 1. Java 字串 (UTF-16) 轉 C 字串 (UTF-8)
const char* str = (*env)->GetStringUTFChars(env, string, 0);
// 2. C 字串操作:前綴 "hello"
char cap[128] = "hello";
strcat(cap, str);
// 3. 釋放 JNI 佔用的記憶體 (關鍵!防止記憶體洩漏)
(*env)->ReleaseStringUTFChars(env, string, str);
// 4. C 字串轉 Java 字串返回
return (*env)->NewStringUTF(env, cap);
}
Windows (MinGW/GCC):
gcc -I "D:\Program Files\Java\jdk1.8.0_45\include" \
-I "D:\Program Files\Java\jdk1.8.0_45\include\win32" \
--shared Test2.c -o Test2.dll
Linux:
gcc -fPIC -shared -I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/linux Test2.c -o libTest2.so
java Test # 輸出:helloworld
| 操作 | 函數 | 說明 |
|---|---|---|
| Java→C 字串 | GetStringUTFChars | 分配本地記憶體,必須配對 Release |
| 釋放字串 | ReleaseStringUTFChars | 歸還記憶體,否則洩漏 |
| C→Java 字串 | NewStringUTF | 建立新 Java String 物件 |
| 陣列操作 | Get/Release | 同理需配對釋放 |
UnsatisfiedLinkError:庫名稱錯誤 (Windows 需 Test2.dll,載入時 loadLibrary("Test2"))、庫未在 java.library.path、32/64 位不匹配ClassFormatError:javah 作用於 .java 而非 .class 文件ReleaseStringUTFChars 或 ReleaseArrayElements GetStringChars/NewStringnative 方法的 Java 類 → javac 編譯javah 生成 .h 頭文件System.loadLibrary 載入庫掌握 JNI 能讓 Java 突破語言邊界,複用高效能 C/C++ 算法庫、系統級 API,是高階 Java 開發者必備技能。