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

Java JNI 入門實戰(上):開發流程、HelloWorld 完整範例

     編輯:Java 程式設計入門

1. JNI 存在意義

  • 標準類庫不支援平台相關特性(硬體控制、OS API、高效能數學庫)。
  • 復用現有 C/C++ 庫(影像處理、加密、壓縮、遊戲引擎)。
  • 效能熱點下沉至原生代碼(組件語言、SIMD 指令集)。

2. JNI 標準開發流程(6 步)

  1. 編寫含 native 方法的 Java 類別
  2. javac 編譯產生 .class
  3. javac -h(JDK 8+)或 javah -jni(JDK 8 前)生成 C/C++ 頭文件 .h
  4. 用 C/C++ 實現原生方法
  5. 編譯生成動態庫(Windows .dll、Linux .so、macOS .dylib)
  6. Java 執行時自動載入庫(System.loadLibrary)並運行

3. 完整 HelloWorld 範例

步驟 1:Java 類別

// HelloWorld.java
public class HelloWorld {
    // 宣告原生方法,無實作
    public native void displayHelloWorld();

    // 靜態區塊載入動態庫
    static {
        System.loadLibrary("hello"); // 對應 hello.dll / libhello.so
    }

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

步驟 2:編譯

javac HelloWorld.java

步驟 3:生成頭文件

# JDK 8+
javac -h . HelloWorld.java

# JDK 8 前
javah -jni HelloWorld

產生 HelloWorld.h 關鍵內容:

/* DO NOT EDIT - machine generated */
#include 
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    displayHelloWorld
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
  (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

命名規則: Java_包名_類名_方法名(包名以 _ 分隔,無包名則直接類名)。

步驟 4:C 實現

// HelloWorldImpl.c
#include 
#include "HelloWorld.h"
#include 

JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
  (JNIEnv *env, jobject obj) {
    printf("Hello World from C!\n");
    return;
}

關鍵點:

  • 包含 jni.h(位於 $JAVA_HOME/include、$JAVA_HOME/include/win32 或 linux)。
  • 包含生成的 .h 確保簽名一致。
  • 函數簽名必須與頭文件完全一致。

步驟 5:生成動態庫

Windows (MSVC cl.exe)

cl -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -LD HelloWorldImpl.c -Fehello.dll
  • -I 指定頭文件搜尋路徑
  • -LD 生成 DLL
  • -Fe 指定輸出檔名(必須與 loadLibrary 參數一致)

Linux/macOS (GCC/Clang)

gcc -shared -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" \
    HelloWorldImpl.c -o libhello.so
# macOS
clang -shared -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/darwin" \
    HelloWorldImpl.c -o libhello.dylib

步驟 6:運行

# Windows
java -Djava.library.path=. HelloWorld

# Linux/macOS
java -Djava.library.path=. HelloWorld
# 或設定 LD_LIBRARY_PATH / DYLD_LIBRARY_PATH

輸出:Hello World from C!

4. 關鍵概念補充

  • JNIEnv*:每執行緒獨立,提供所有 JNI 函數表指標。
  • jobject:傳入的 Java 物件引用(對應 this)。
  • static 載入塊:確保庫在首次使用前載入,且僅載入一次。
  • 庫命名:System.loadLibrary("name") 自動添加平台前綴/後綴(libname.so、name.dll)。

5. 常見坑點避雷

  1. UnsatisfiedLinkError: 库名不符、路徑錯誤、架構不匹配(32/64 位)、依賴庫缺失。
  2. 頭文件不匹配: Java 類別修改後未重新生成 .h 並重新編譯 C 代碼。
  3. 簽名錯誤: 手寫 C 函數名與 javah 生成不一致。
  4. 記憶體洩漏: GetStringUTFChars/Get*ArrayElements 未配對 Release。
  5. 異常處理: JNI 代碼拋出 C++ 異常會導致 JVM 崩潰,必須捕獲並轉為 Java 異常(ThrowNew)。

總結

JNI 是 Java 呼叫原生代碼的標準橋樑。掌握「Java 宣告 → 編譯 → 生成頭文件 → C 實現 → 編譯動態庫 → 載入運行」標準流程,配合 javac -h 自動化頭文件生成,即可高效構建高效能、跨平台的混合應用。下篇將深入類型映射、字串陣列操作、方法與欄位存取等進階主題。

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