java中的native關鍵字作用 native本地方法的2個實現要點

Java 中的 native 關鍵字用于調用非 java 語言實現的函數,1. 允許聲明無實現的方法,2. 要求使用 c++/c++ 和 jni 實現并鏈接,3. 提供訪問底層資源和提升性能的能力。native 方法通過 jni 映射到 native 函數,并借助 jnienv 指針jvm 交互,盡管帶來上下文切換和類型轉換的開銷,但可通過減少調用次數、使用直接內存等優化策略緩解。此外,native 代碼需手動處理異常并通過 thrownew 拋出 java 異常后立即返回以避免崩潰。

java中的native關鍵字作用 native本地方法的2個實現要點

Java 中的 native 關鍵字,簡單來說,它允許 Java 代碼調用用其他語言(通常是 C 或 C++)編寫的函數。這使得 Java 應用程序可以訪問底層系統資源或利用現有非 Java 代碼庫。

java中的native關鍵字作用 native本地方法的2個實現要點

native 方法實現的兩個要點:

java中的native關鍵字作用 native本地方法的2個實現要點

  1. 聲明和鏈接: 在 Java 類中聲明 native 方法,但不提供實現。然后,你需要使用 C 或 C++ 編寫實現,并使用 JNI (Java Native interface) 將 Java 方法簽名映射到 C/C++ 函數。最后,將編譯后的 C/C++ 代碼鏈接到 Java 應用程序中。
  2. JNI 的使用: JNI 是 Java 提供的一套接口,用于在 Java 和 native 代碼之間進行交互。你需要理解 JNI 的數據類型映射、對象引用管理以及異常處理機制。

如何理解 native 關鍵字?為什么需要它?

立即學習Java免費學習筆記(深入)”;

java中的native關鍵字作用 native本地方法的2個實現要點

native 關鍵字的存在,某種程度上,是 Java 追求“一次編寫,到處運行”的理想與現實妥協的產物。 理想很豐滿,現實卻骨感。總有些時候,我們需要直接操作硬件,或者使用一些性能極致優化的庫,而這些往往是用 C/C++ 實現的。 比如,圖形渲染、音視頻編解碼,或者是一些底層的系統調用。

想象一下,如果你要寫一個高性能的圖像處理程序,完全用 Java 實現,效率可能遠不如直接調用底層的 C++ 庫。 native 關鍵字就提供了這樣一座橋梁,讓 Java 代碼可以調用這些 C/C++ 代碼,從而獲得更高的性能或者訪問底層資源的能力。

為什么不直接用 C/C++ 寫?

當然,你可以直接用 C/C++ 寫,但 Java 的優勢在于其跨平臺性、垃圾回收機制、以及豐富的類庫。 使用 Java 結合 native 方法,可以兼顧兩者的優點。

如何編寫和調用一個簡單的 native 方法?

假設我們有一個 Java 類,需要調用一個 C 函數來計算兩個整數的和。

1. Java 代碼:

public class NativeAdder {     static {         System.loadLibrary("nativeadder"); // 加載 native 庫     }      public native int add(int a, int b); // 聲明 native 方法      public static void main(String[] args) {         NativeAdder adder = new NativeAdder();         int sum = adder.add(5, 3);         System.out.println("Sum: " + sum);     } }

2. C 代碼 (nativeadder.c):

#include <jni.h> #include "NativeAdder.h" // 根據 Java 類生成頭文件  JNIEXPORT jint JNICALL Java_NativeAdder_add(JNIEnv *env, jobject obj, jint a, jint b) {     return a + b; }

3. 生成頭文件:

使用 javac -h . NativeAdder.java 命令生成 NativeAdder.h 頭文件。

4. 編譯 C 代碼:

使用 C 編譯器(例如 GCC)將 nativeadder.c 編譯成動態鏈接庫(例如 nativeadder.so 或 nativeadder.dll)。

5. 運行 Java 代碼:

確保動態鏈接庫在 Java 的 library path 中,然后運行 Java 程序。

JNIEnv 指針是什么?它有什么作用?

JNIEnv 是一個指向 JNI 函數表的指針。這個函數表包含了所有用于在 native 代碼中與 Java 虛擬機交互的函數。 換句話說,你可以通過 JNIEnv 指針來訪問 Java 對象、調用 Java 方法、拋出 Java 異常等等。

你可以把它想象成一個“翻譯器”,它負責把 Java 世界的規則翻譯成 C/C++ 世界能理解的語言,反之亦然。 比如,你需要創建一個 Java 字符串對象,你就需要通過 JNIEnv 指針調用 NewStringUTF 函數。

JNI 的性能開銷有多大?如何優化?

JNI 調用確實會帶來一定的性能開銷,因為涉及到 Java 和 native 代碼之間的上下文切換、數據類型轉換等操作。 這種開銷在頻繁調用的場景下會變得比較明顯。

優化 JNI 性能的一些方法:

  • 減少 JNI 調用次數: 盡量將多個操作合并到一個 native 方法中執行,避免頻繁的跨語言調用。
  • 使用直接內存 (Direct Memory): 直接內存是 JVM 外的一塊內存區域,native 代碼可以直接訪問,避免了數據在 Java 堆和 native 堆之間的復制。
  • 緩存 JNI 方法 ID: JNI 方法 ID 用于標識 Java 方法,在第一次調用時需要查找。 可以將方法 ID 緩存起來,避免重復查找。
  • 使用 primitive 類型: 盡量使用 primitive 類型(例如 int、Float)而不是對象類型(例如 Integer、Float),因為 primitive 類型的轉換開銷更小。
  • 避免不必要的對象創建: 在 native 代碼中創建 Java 對象會帶來額外的開銷,盡量避免不必要的對象創建。

JNI 的異常處理機制是怎樣的?

在 native 代碼中,如果發生異常,你需要使用 JNI 函數 ThrowNew 或 Throw 拋出 Java 異常。 拋出異常后,native 方法應該立即返回,避免執行任何可能導致程序崩潰的操作。 JVM 會自動處理拋出的異常,并將其傳遞給 Java 代碼中的異常處理程序。

需要注意的是,在 native 代碼中拋出異常并不會立即終止程序的執行。 你需要顯式地返回,讓 JVM 來處理異常。 如果 native 方法沒有返回,JVM 可能會崩潰。

? 版權聲明
THE END
喜歡就支持一下吧
點贊10 分享