Java Native Interface (JNI) 是 JDK 的核心組件,提供 Java 程式呼叫本地原生代碼(如 C/C++、組合語言)的標準介面。透過 JNI,運行於 JVM 上的 Java 代碼能夠操作系統級別的函式庫,甚至將 JVM 嵌入原生應用程式中,實現跨平台的無縫整合。本文將以具體範例,詳細示範如何使用 Visual C++ 6.0 完成 JNI 開發的全流程。
在 Java 中呼叫 Windows API 的 MessageBox 函式,並傳遞字串參數顯示於對話方塊中。這將展示 Java 如何突破語言限制,直接調用作業系統原生功能。
首先建立 WinMsgBox.java,宣告需由本地代碼實作的 showMsgBox 方法:
// WinMsgBox.java
package edu.netcom.jni;
public class WinMsgBox {
static {
System.loadLibrary("WinMsgDll"); // (1) 載入動態連結庫
}
public native void showMsgBox(String str); // (2) 宣告 native 方法
}
關鍵點說明:
WinMsgDll 為 DLL 檔名(不含副檔名),JVM 會依平台自動補全(Windows 為 .dll,Solaris 為 .so)。DLL 必須置於 java.library.path 指定的目錄,常見路徑包含 Windows\system32、JDK bin 目錄、或主類別所在的當前目錄。native 關鍵字標示此方法由本地代碼實作。執行指令:
javac -d . WinMsgBox.java
將於當前目錄產生 edu\netcom\jni\WinMsgBox.class。
使用 javah 工具生成 C/C++ 標頭檔:
javah -jni edu.netcom.jni.WinMsgBox
產生 edu_netcom_jni_WinMsgBox.h,檔名規則為「套件名_類別名」,底線分隔。標頭檔內容包含 JNI 函式原型:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class edu_netcom_jni_WinMsgBox */
#ifndef _Included_edu_netcom_jni_WinMsgBox
#define _Included_edu_netcom_jni_WinMsgBox
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: edu_netcom_jni_WinMsgBox
* Method: showMsgBox
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
重點解析:
jni.h 位於 JDK include 目錄。(Ljava/lang/String;)V 表示參數為 String、回傳 void。Java_套件名_類別名_方法名。所有 native 方法會自動增加兩個參數:JNIEnv*(JNI 環境指標)與 jobject(呼叫物件參考)。jni.h 定義的本地類型(如 jstring、jint 等)。File → New → Projects 選擇 Win32 Dynamic-Link Library,專案名稱設為 WinMsgDll(需與 Step 1 一致),選擇 An empty DLL project。Tools → Options → Directories 新增 JDK 的 include 與 include\win32 目錄(如 D:\J2SDK1.4.2_03\INCLUDE)。edu_netcom_jni_WinMsgBox.h 複製至專案目錄並加入專案。WinMsgDll.cpp 實作本地方法:#include "windows.h"
#include "edu_netcom_jni_WinMsgBox.h"
JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox
(JNIEnv *env, jobject obj, jstring str) {
const char *msg;
msg = env->GetStringUTFChars(str, 0); // 將 jstring 轉為 UTF-8 C 字串
MessageBox(NULL, msg, "Java invoke", MB_OK); // 呼叫 Windows API
env->ReleaseStringUTFChars(str, msg); // 釋放資源
}
WinMsgDll.dll,並複製至 Step 1 指定的庫搜尋路徑(建議與測試類別同目錄)。JNI 使用 JNIEXPORT(展開為 __declspec(dllexport))匯出函式,但編譯器可能產生修飾名稱導致找不到符號。建議額外提供 .def 模組定義檔強制指定匯出名稱:
LIBRARY "WinMsgDll"
DESCRIPTION 'message Windows Dynamic Link Library'
EXPORTS
Java_edu_netcom_jni_WinMsgBox_showMsgBox
// TestJNI.java
import edu.netcom.jni.WinMsgBox;
public class TestJNI {
public static void main(String[] args) {
WinMsgBox box = new WinMsgBox();
box.showMsgBox("Wonderful!!");
}
}
編譯執行後,Windows 訊息方塊將跳出顯示「Wonderful!!」,標誌著 JNI 整合成功。
java.library.path 包含 DLL 所在目錄,否則會拋出 UnsatisfiedLinkError。GetStringUTFChars/ReleaseStringUTFChars 配對管理記憶體,避免洩漏。掌握此流程,即可讓 Java 應用程式輕鬆呼叫平台專屬的高效能原生代碼,發揮「Write Once, Run Anywhere」與系統級效能並重的優勢。