首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JNI 實戰:使用 Native 方法擴展 Java 功能 (含完整編譯部署步驟)

     編輯:Java 相關知識

Java 無法直接存取作業系統底層 (硬體、系統呼叫、舊有 C/C++ 庫)。JNI (Java Native Interface) 提供橋樑:Java 宣告 native 方法 → javah 產生 C 標頭檔 → C/C++ 實作 → 編譯為動態庫 → System.loadLibrary 載入。本文以「Hello World」為例,完整演示 Windows + Visual C++ 命令列編譯全流程。

一、適用場景

  1. 存取平台特有功能 (系統資訊、硬體控制、登錄檔等)
  2. 整合既有 C/C++ 庫 (數學運算、圖像處理、加密演算法)
  3. 效能熱點下沉 (極少數經證實的瓶頸,權衡可移植性後採用)

二、標準 5 步驟流程

1. Java 宣告 native 方法


package com.hode.hodeframework.modelupdate;

public class CheckFile {
    public native void displayHelloWorld();

    static {
        System.loadLibrary("test"); // 載入 test.dll (Windows) / libtest.so (Linux)
    }

    public static void main(String[] args) {
        new CheckFile().displayHelloWorld();
    }
}

2. 編譯 Java 並產生 C 標頭檔


# 進入 class 輸出根目錄 (如 bin/ 或 classes/)
javac com/hode/hodeframework/modelupdate/CheckFile.java
javah -jni com.hode.hodeframework.modelupdate.CheckFile
# 產生 com_hode_hodeframework_modelupdate_CheckFile.h

3. 實作 C 函式 (對應標頭檔原型)


// com_hode_hodeframework_modelupdate_CheckFile.c
#include "com_hode_hodeframework_modelupdate_CheckFile.h"
#include 

JNIEXPORT void JNICALL 
Java_com_hode_hodeframework_modelupdate_CheckFile_displayHelloWorld(
    JNIEnv *env, jobject obj) {
    printf("Hello world from C!\n");
    return;
}
  • 函式命名規則:`Java_包名_類別名_方法名` (點號換底線)
  • 參數:`JNIEnv*` (JNI 環境指標,提供回呼 Java 的 API)、`jobject` (呼叫物件 `this` 或 `class` 物件)

4. 編譯為動態庫 (Windows + MSVC 命令列)


cl -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" \
   -LD com_hode_hodeframework_modelupdate_CheckFile.c \
   -Fe test.dll
  • -I:指定 JDK 標頭檔路徑 (jni.h, jni_md.h)
  • -LD:建立 DLL
  • -Fe:指定輸出檔名 (需與 `loadLibrary("test")` 一致)

Linux/GCC 範例:


gcc -shared -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" \
    com_hode_hodeframework_modelupdate_CheckFile.c -o libtest.so

5. 執行 Java 程式 (指定庫路徑)


java -Djava.library.path=. com.hode.hodeframework.modelupdate.CheckFile
# 輸出: Hello world from C!

三、進階關鍵技術

主題關鍵 API / 概念
Java 物件操作GetFieldID, GetMethodID, CallVoidMethod, NewObject, Get/Set<Type>Field
陣列處理GetArrayLength, Get<Type>ArrayElements, Release<Type>ArrayElements, New<Type>Array
字串處理NewStringUTF, GetStringUTFChars, ReleaseStringUTFChars (注意 UTF-8/UTF-16 轉換)
異常拋出ThrowNew, Throw, ExceptionCheck, ExceptionClear
全域/區域引用NewGlobalRef, DeleteGlobalRef, NewLocalRef (防止 GC 回收 Native 持有的物件)
線程附著AttachCurrentThread, DetachCurrentThread (非 Java 建立的線程呼叫 JNI 必須附著)

四、常見坑點與最佳實踐

  • UnsatisfiedLinkError:庫路徑錯誤、庫名不符、依賴缺失 (Dependency Walker 排查)、32/64 位元不匹配
  • 記憶體洩漏:未釋放 `GetStringUTFChars`/`GetPrimitiveArrayCritical`、全域引用未刪除
  • 崩潰不留堆疊:Native 端 Segfault 直接殺死 JVM,建議單元測試 C 代碼、使用 AddressSanitizer
  • 效能權衡:JNI 呼叫開銷大 (參數封裝、線程切換),細粒度呼叫得不償失,需批量化介面設計
  • 可移植性:平台相關代碼隔離於 Native 層,Java 層保持純平台無關

五、現代替代方案

  • JNA (Java Native Access):純 Java 透過 libffi 直接呼叫動態庫,**無需撰寫 C 代碼/編譯**,開發效率極高
  • JNR-FFI:類似 JNA,效能更佳,支援更多型別
  • GraalVM Native Image / Polyglot:將 Java 編譯為原生執行檔,或同進程互通 C++/Python/R 等語言

除非極度依賴 JNI 細粒度控制,**新專案優先評估 JNA/JNR**,大幅降低維護成本。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved