Java 原生介面(JNI)是連結 Java 與 C/C++ 的橋樑,讓 Java 程式能呼叫原生程式庫,提升效能或處理 Java 難以完成的任務。本文將透過一個完整範例,示範如何在 Java 中呼叫 C 函數,涵蓋從撰寫 Java 類別、產生標頭檔、實作 C 程式、編譯成共享庫,到最終執行的完整流程。
建立 J2C.java,宣告 native 方法並載入共享庫:
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
public class J2C {
static {
try {
System.loadLibrary("j2c"); // 載入 libj2c.so
} catch (UnsatisfiedLinkError e) {
System.err.println("無法載入 J2C 函式庫: " + e);
}
}
// 宣告 native 方法
public static native int write2proc(int pid);
public static void main(String[] args) {
// 取得目前進程 PID
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
String info = runtime.getName(); // 格式:pid@hostname
int index = info.indexOf("@");
if (index != -1) {
int pid = Integer.parseInt(info.substring(0, index));
System.out.println("Process info: " + info);
System.out.println("PID: " + pid);
write2proc(pid); // 呼叫 C 函數
}
try { Thread.sleep(8000); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
編譯:javac J2C.java 產生 J2C.class。
使用 javah 工具(JDK 8 及以前)或 javac -h(JDK 9+)產生標頭檔:
javah J2C
產生 J2C.h,內容如下(勿手動修改):
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class J2C */
#ifndef _Included_J2C
#define _Included_J2C
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: J2C
* Method: write2proc
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_J2C_write2proc
(JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif
建立 J2C.c,實作標頭檔中宣告的函數:
#include <stdio.h>
#include "J2C.h"
JNIEXPORT jint JNICALL Java_J2C_write2proc(JNIEnv *env, jclass cls, jint pid) {
printf("Current PID from C: %d\n", pid);
return 0;
}
編譯時需指定 JDK 的 include 路徑(含 jni.h 與平台相關的 jni_md.h):
# Linux 範例(依實際 JDK 路徑調整)
gcc -I/usr/lib/jvm/java-8-openjdk-amd64/include \
-I/usr/lib/jvm/java-8-openjdk-amd64/include/linux \
-fPIC -c J2C.c -o J2C.o
# 產生共享庫
gcc -shared -Wl,-soname,libj2c.so.1 -o libj2c.so.1.0 J2C.o
# 建立連結檔供 System.loadLibrary 使用
cp libj2c.so.1.0 libj2c.so
注意:System.loadLibrary("j2c") 會自動尋找 libj2c.so(Linux/macOS)或 j2c.dll(Windows)。
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
java J2C
輸出範例:
Process info: 12345@myhost
PID: 12345
Current PID from C: 12345
javah 產生標頭檔以避免錯誤。System.loadLibrary("name") 對應 libname.so(Linux/macOS)或 name.dll(Windows)。-I$JAVA_HOME/include 及平台專屬目錄,否則找不到 jni.h。ExceptionCheck() 並妥善處理。javac -h . J2C.java 直接產生標頭檔,無需 javah。GetStringUTFChars、GetIntArrayElements),並在用畢後釋放。掌握 JNI 可讓 Java 擴展至高效能運算、硬體控制、舊有 C/C++ 程式庫復用等領域,是 Java 開發者的重要進階技能。