JNI (Java Native Interface) 是 Java 平台的關鍵特性,彌補了「跨平台」帶來的平台無關性限制,讓 Java 能調用 C/C++ 靜態庫,發揮原生效能、復用既有代碼、存取硬體資源。

native 方法javac 編譯生成 .classjavah -jni 生成 C/C++ 標頭檔.so/.dll/.dylib)System.loadLibrary 載入庫並調用package com.magc.jni;
public class HelloWorld {
static {
System.loadLibrary("Hello"); // 載入 libHello.so / Hello.dll
}
public native void displayHello(); // 原生方法宣告
public static void main(String[] args) {
new HelloWorld().displayHello();
}
}
cd src
javac ./com/magc/jni/HelloWorld.java
javah -jni com.magc.jni.HelloWorld
產出 com_magc_jni_HelloWorld.h:
/* DO NOT EDIT - machine generated */
#include <jni.h>
#ifndef _Included_com_magc_jni_HelloWorld
#define _Included_com_magc_jni_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_com_magc_jni_HelloWorld_displayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
關鍵點:函數名 Java_包名_類名_方法名 嚴格對應,JNIEnv* 提供 JNI 函數表指標,jobject 對應 Java 這個實例(this)。
#include <jni.h>
#include "com_magc_jni_HelloWorld.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_com_magc_jni_HelloWorld_displayHello
(JNIEnv *env, jobject obj) {
printf("From jni_helloworldImpl.cpp :");
printf("Hello world ! \n");
return;
}
Linux (Ubuntu):
g++ -shared -fPIC -I /usr/lib/jvm/java-6-openjdk/include \
-I /usr/lib/jvm/java-6-openjdk/include/linux \
jni_helloworldImpl.cpp -o libHello.so
Windows (MinGW):
g++ -shared -I "C:\Program Files\Java\jdk\include" \
-I "C:\Program Files\Java\jdk\include\win32" \
jni_helloworldImpl.cpp -o Hello.dll
macOS:
g++ -dynamiclib -I /Library/Java/JavaVirtualMachines/jdk/Contents/Home/include \
-I /Library/Java/JavaVirtualMachines/jdk/Contents/Home/include/darwin \
jni_helloworldImpl.cpp -o libHello.jnilib
java -Djava.library.path=. com.magc.jni.HelloWorld
輸出:
From jni_helloworldImpl.cpp :Hello world !
FindClass、GetMethodID、CallObjectMethod、NewStringUTF、GetStringUTFChars 等| Java 類型 | JNI 類型 | C/C++ 類型 |
|---|---|---|
| boolean | jboolean | unsigned char |
| byte | jbyte | signed char |
| char | jchar | unsigned short |
| short | jshort | short |
| int | jint | int / int32_t |
| long | jlong | long long / int64_t |
| float | jfloat | float |
| double | jdouble | double |
| String | jstring | opaque |
| Object | jobject | opaque |
| int[] | jintArray | opaque |
// 錯誤:直接用 GetStringUTFChars 取得指標後未 Release
const char* cstr = env->GetStringUTFChars(jstr, NULL);
// ... 使用 cstr
env->ReleaseStringUTFChars(jstr, cstr); // 必須釋放!
// 正確:使用 GetStringCritical / ReleaseStringCritical (較快,但期間不能阻塞)
// 或 NewStringUTF 直接建立新 jstring 回傳
// C++ 端拋出 Java 異常
jclass excClass = env->FindClass("java/lang/IllegalArgumentException");
env->ThrowNew(excClass, "Invalid input from JNI");
// Java 端捕獲
try {
nativeMethod();
} catch (IllegalArgumentException e) { ... }
GetPrimitiveArrayCritical) 直接存取陣列記憶體(需極短時間內釋放)FindClass 昂貴,靜態快取UnsatisfiedLinkError:庫名錯誤(lib 前綴、.so/.dll 後綴)、路徑錯誤、架構不匹配(32/64 位)、依賴庫缺失JNI_OnLoad 未實現導致版本不匹配NewGlobalRef 未 DeleteGlobalRefAttachCurrentThread