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

Java JNI 呼叫 C 函數完整實作教學

     編輯:Java 相關知識

前言

Java 原生介面(JNI)是連結 Java 與 C/C++ 的橋樑,讓 Java 程式能呼叫原生程式庫,提升效能或處理 Java 難以完成的任務。本文將透過一個完整範例,示範如何在 Java 中呼叫 C 函數,涵蓋從撰寫 Java 類別、產生標頭檔、實作 C 程式、編譯成共享庫,到最終執行的完整流程。

環境準備

  • JDK(已設定 JAVA_HOME 與 PATH)
  • GCC 編譯器(Linux/Mac)或 MinGW(Windows)
  • 基本的 Java 與 C 語言知識

實作步驟

1. 撰寫並編譯 Java 程式

建立 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。

2. 產生 C/C++ 標頭檔

使用 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

3. 實作 C 程式

建立 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;
}

4. 編譯 C 程式並產生共享庫

編譯時需指定 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)。

5. 設定庫路徑並執行

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
java J2C

輸出範例:

Process info: 12345@myhost
PID: 12345
Current PID from C: 12345

關鍵要點總結

  1. 方法簽名必須一致:Java native 方法與 C 實作的函數名稱、參數類型、回傳類型需完全對應,建議透過 javah 產生標頭檔以避免錯誤。
  2. 共享庫命名規則:System.loadLibrary("name") 對應 libname.so(Linux/macOS)或 name.dll(Windows)。
  3. 編譯參數:務必加入 -I$JAVA_HOME/include 及平台專屬目錄,否則找不到 jni.h。
  4. 例外處理:C 程式碼中若呼叫 JNI 函數可能拋出例外,需檢查 ExceptionCheck() 並妥善處理。

進階提示

  • JDK 9+ 可使用 javac -h . J2C.java 直接產生標頭檔,無需 javah。
  • 複雜專案建議使用 CMake 或 Gradle 整合原生建置流程。
  • 傳遞陣列、字串、物件時需使用 JNI 函數(如 GetStringUTFChars、GetIntArrayElements),並在用畢後釋放。

掌握 JNI 可讓 Java 擴展至高效能運算、硬體控制、舊有 C/C++ 程式庫復用等領域,是 Java 開發者的重要進階技能。

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