native關鍵字說明其修飾的方法是一個原生態方法,方法對應的實現不是在當前文件,而是在用其他語言(如C和C++)實現的文件中。Java語言本身不能對操作系統底層進行訪問和操作,但是可以通過JNI接口調用其他語言來實現對底層的訪問。
凡是一種語言,都希望是純。比如解決某一個方案都喜歡就單單這個語言來寫即可。Java平臺有個用戶和本地C代碼進行互操作的API,稱為Java Native Interface (Java本地接口)。
http://www.cnblogs.com/Alandre/p/4456719.html
創建一個Java類,里面包含著一個 native 的方法和加載庫的方法 loadLibrary。HelloNative.java 代碼如下:
native 關鍵字告訴編譯器(其實是JVM)調用的是該方法在外部定義,這里指的是C。
public class HelloNative
{static{System.loadLibrary("HelloNative");}public static native void sayHello();
@SuppressWarnings("static-access")
public static void main(String[] args){new HelloNative().sayHello();}}
那個加載庫的到后面也起作用。native 關鍵字告訴編譯器(其實是JVM)調用的是該方法在外部定義,這里指的是C。如果大家直接運行這個代碼,? JVM會告之:“A Java Exception has occurred.”控制臺輸出如下:Exception in thread "main" java.lang.UnsatisfiedLinkError: no HelloNative in java.library.path?at java.lang.ClassLoader.loadLibrary(Unknown Source)?at java.lang.Runtime.loadLibrary0(Unknown Source)?at java.lang.System.loadLibrary(Unknown Source)?at HelloNative.(HelloNative.java:5)
運行javah,得到包含該方法的C聲明頭文件.h ? ?->
根據頭文件,寫C實現本地方法 ?->
生成dll共享庫,然后Java程序load庫,調用即可。
HelloNative.h文件:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class HelloNative */
#ifndef _Included_HelloNative
#define _Included_HelloNative
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:???? HelloNative
* Method:??? sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloNative_sayHello
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
jni.h 這個文件,在/%JAVA_HOME%include
3、根據頭文件,寫C實現本地方法。
這里我們簡單地實現這個sayHello方法如下:
#include "HelloNative.h"
#include
JNIEXPORT void JNICALL Java_HelloNative_sayHello
{printf("Hello,JNI");}
生成dll共享庫,然后Java程序load庫,調用即可。在Windows上,MinGW GCC 運行如下:
gcc -m64? -Wl,--add-stdcall-alias -I"C:\Program Files\Java\jdk1.7.0_71\include" -I"C:\Program Files\Java\jdk1.7.0_71\include\include\win32" -shared -o HelloNative.dll HelloNative.c
-m64表示生成dll庫是64位的。然后運行HelloNative: java HelloNative
JNI調用C 流程
JNI是Java本機接口(JavaNativeInterface),是一個本機編程接口,它是Java軟件開發工具箱(JavaSoftware Development Kit,SDK)的一部分。JNI允許Java代碼使用以其他語言編寫的代碼和代碼庫。Invocation API(JNI的一部分)可以用來將Java虛擬機(JVM)嵌入到本機應用程序中,從而允許程序員從本機代碼內部調用Java代碼。
不過,對Java外部的調用通常不能移植到其他平臺,在applet中還可能引發安全異常。實現本地代碼將使您的Java應用程序無法通過100%純Java測試。但是,如果必須執行本地調用,則要考慮幾個準則:
1.將您的所有本地方法都封裝到一個類中,這個類調用單個的DLL。對每一種目標操作系統平臺,都可以用特定于適當平臺的版本的DLL。這樣可以將本地代碼的影響減少到最小,并有助于將以后所需要的移植問題考慮在內。
2.本地方法盡量簡單。盡量使您的本地方法對第三方(包括Microsoft)運行時DLL的依賴減少到最小。使您的本地方法盡量獨立,以將加載您的DLL和應用程序所需的開銷減少到最小。如果需要運行時DLL,必須隨應用程序一起提供。
JNI的書寫步驟如下:
a.編寫帶有native聲明的方法的Java類
b.使用javac命令編譯編寫的Java類
c.使用java-jni ****來生成后綴名為.h的頭文件
d.使用其他語言(C、C++)實現本地方法
e.將本地方法編寫的文件生成動態鏈接庫
以下是一個在Java中調用本地C程序的簡單的例子:
a.編寫HelloWorld.java類
class HelloWorld{
publicnativevoid hello();
static{
System.loadLibrary("hello");
}
public static void main(String[] args){
new HelloWorld().hello();
}
}
聲明native方法:如果你想將一個方法做為一個本地方法的話,那么你就必須聲明改方法為native的,并且不能實現。其中方法的參數和返回值在后面講述。
Load動態庫:System.loadLibrary("hello");加載動態庫(我們可以這樣理解:我們的方法hello()沒有實現,但是我們在下面就直接使用了,所以必須在使用之前對它進行初始化)這里一般是以static塊進行加載的。同時需要注意的是System.loadLibrary();的參數“hello”是動態庫的名字。
b.編譯
javac HelloWorld.java
c.生成.h文件
javah -jni HelloWorld
示例比如說 類文件在D:\project\sparkStreamingLearn\src\main\java\TestNative\HelloWorldnative.java
使用javah 時注意執行位置是源代碼目錄 D:\project\sparkStreamingLearn\src\,
classpath是 載入類的路徑 是 D:\project\sparkStreamingLearn\src\main\java\
對應的類為:TestNative包下的HelloWorldnative.java? -jni 路徑是包名+類名 即 TestNative\HelloWorldnative.java
-d 輸出目錄(可以任意指定) 即TestNative_HelloWorldnative.h文件的輸出目錄 當前可以指定為D:\project\sparkStreamingLearn\src\main\java\TestNative\
結果為: D:\project\sparkStreamingLearn\src\main\java\TestNative\TestNative_HelloWorldnative.h
我們要開始寫javah的命令,以便生成對應的C語言頭文件
D:\我的文檔\workspace\PrepareForExam\src>javah -classpath D:\我的文檔\workspace\PrepareForExam\bin -d d:/ -jni
com.example.myclass.jni_test
其中java中各個命令的意思是
-classpath <路徑> 用于裝入類的路徑
-d <目錄> 輸出目錄
-jni 生成 JNI樣式的頭文件(默認)
注意到以上我們命令中指定的路徑
注意到我們的命令符的執行位置是源代碼目錄”D:\我的文檔\workspace\PrepareForExam\src”
-classpath? 后面的路徑是指包”com.example.myclass”所在的根路徑
-jni 后面的路徑是包名+類名
生成內容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:???? HelloWorld
* Method:????hello
* Signature: ()V
*/
JNIEXPORT void JNICALLJava_HelloWorld_hello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
第一個參數是調用JNI方法時使用的JNI Environment指針。第二個參數是指向在此Java代碼中實例化的Java對象HelloWorld的一個句柄。其他參數是方法本身的參數
d.c實現
#include
#include "HelloWorld.h"
#include
JNIEXPORT void JNICALLJava_HelloWorld_hello(JNIEnv *env,jobject obj){
printf("Hello World!\n");
return;
}
其中,第一行是將jni.h文件引入(在%JAVA_HOME%\include目錄下),里邊有JNIEnv和jobject的定義。
e.編譯c實現
cl -- vs201X 的安裝目錄下 Common7/Tools/Shortcuts/VS2013開發人員命令提示? 開始cl 命令行? c/c++ 編譯器 ?這里以在Windows中為例,需要生成dll文件。在保存HelloWorldImpl.c文件夾下面,使用VC的編譯器cl成。是-I? 搜索其后添加所需文件所在的目錄 注意需要添加"" 即"java_home%\include" -LD? 是創建.dll
這里以在Windows中為例,需要生成dll文件。在保存HelloWorldImpl.c文件夾下面,使用VC的編譯器cl成。
cl -I%java_home%\include -I%java_home%\include\win32 -LD HelloWorldImp.c -Fehello.dll
注意:生成的dll文件名在選項-Fe后面配置,這里是hello,因為在HelloWorld.java文件中我們loadLibary的時候使用的名字是hello。當然這里修改之后那里也需要修改。另外需要將-I%java_home%\include -I%java_home%\include\win32參數加上,因為在第四步里面編寫本地方法的時候引入了jni.h文件。
6) 運行程序
javaHelloWorld就ok了!
javac? 出現錯誤 不是內部或外部命令,也不時可運行的程序? 修改path 添加 %Java_home%/bin;%Java_home%/jre/bin;
javac 出現中文亂碼 修改方法: javac -encoding "UTF-8" HelloWorldnative.java