用javah 編譯頭文件時出錯,提示找不到類或類文件,但明明有,解決辦法注意兩點:
1,編譯時只要進入到包的上層目錄:一般是bin目錄下(如果是用eclipse編譯的java文件的話,bin目錄下會有package)
2,執行下面的命令(建議直接複製粘貼,然後修改自己的package和class_name因爲自己輸的話經常出錯,,,,)
javah -classpath . -jni package_name.class_name
下面的文章轉自:http://www.cnblogs.com/icejoywoo/archive/2012/02/22/2363709.html
JNI是Java Native Interface的英文縮寫, 中文翻譯爲本地調用, 自從Java 1.1開始就成爲了Java標準的一部分.
C/C++是系統級的編程語言, 可以用來開發任何和系統相關的程序和類庫, 但是Java本身編寫底層的應用比較難實現, 使用JNI可以調用現有的本地庫, 極大地靈活了Java的開發.
C/C++的效率是目前最好的語言, 可以使用C/C++來實現一些實時性非常高的部分. C/C++和Java本身都是非常流行的編程語言, 一些大型軟件中經常使用語言之間的混合編程.
鑑於目前網絡上JNI的文章不是特別多, 我將自己的一些總結寫在這裏. 如有錯漏, 歡迎指正!
Java調用C/C++大概有這樣幾個步驟
- 編寫帶有native方法的Java類, 使用javac工具編譯Java類
- 使用javah來生成與native方法對應的頭文件
- 實現相應的頭文件, 並編譯爲動態鏈接庫(windows下是.dll, linux下是.so)
下面就完整的介紹一個簡單的Java調用C/C++的例子, 這個例子是來自http://www.ibm.com/developerworks/cn/education/java/j-jni/index.html, 不過其中有一些錯誤, 這個文章是非常不錯的JNI學習資料, 但是非常古老.
編寫Java類
我們來編寫一個Sample1的java類
其中有一句比較重要, 這句話加載了動態類庫
System.loadLibrary("Sample1");
在windows下加載的就是Sample1.dll, 在linux下加載的就是Sample1.so.
本文使用的windowws, 所以後面使用Sample1.dll來表示Sample1動態鏈接庫.
注意: 不可以在代碼中寫上後綴dll或so. 還要保證Sample1.dll在path路徑中. 這個Sample1.dll是我們後面需要編譯出來的東西.
4個native方法就是我們需要用C來實現的方法.
編譯Sample1.java, 使用命令行(windows是cmd, linux下一般是bash)
>javac Sample1.java
可以看到Sample1.class文件
使用javah生成頭文件
在命令行中運行
>javah Sample1
可以在目錄下看到一個新文件Sample1.h
我們可以看到其中有四個函數聲明, Java_完整類名_方法名, 完整類名包括了包名, 例如demo.Sample1是完整類名, 對應的這裏就是demo_Sample1.
在註釋中我們可以看到這樣一個東西 Signature, 這個是方法的簽名. 關於Signature, 下面通過一個表格來說明.
java類型 | Signature | 備註 |
boolean | Z | |
byte | B | |
char | C | |
short | S | |
int | I | |
long | L | |
float | F | |
double | D | |
void | V | |
object | L用/分割的完整類名 | 例如: Ljava/lang/String表示String類型 |
Array | [簽名 | 例如: [I表示int數組, [Ljava/lang/String表示String數組 |
Method | (參數簽名)返回類型簽名 | 例如: ([I)I表示參數類型爲int數組, 返回int類型的方法 |
上面頭文件的第一個函數聲明
JNIEXPORT jint JNICALL Java_Sample1_intMethod (JNIEnv *, jobject, jint);
註釋中的簽名是 Signature: (I)I
在每個函數的參數列表中都有JNIEnv *和 jobject兩個參數, 這兩個參數稍候說明.
實現頭文件中的函數
可以使用C語言來實現, 也可以使用C++來實現, 下面先說說C語言的實現.
(*env)->GetStringUTFChars()這個方法, 是用來在Java和C之間轉換字符串的, 因爲Java本身都使用了雙字節的字符, 而C語言本身都是單字節的字符, 所以需要進行轉換.
JNIEnv *是每個函數都有的參數, 它包含了很多有用的方法, 使用起來類似Java的反射, 也提供了這樣一個編碼轉換的函數.
GetStringUTFChars()和NewStringUTF(), 第一個是從UTF8轉換爲C的編碼格式, 第二個是根據C的字符串返回一個UTF8字符串.
ReleaseStringUTFChars()是用來釋放對象的, 在Java中有虛擬機進行垃圾回收, 但是在C語言中, 這些對象必須手動回收. 否則可能造成內存泄漏.
函數的名字一眼看到就可以猜出功能, jni.h中的大部分函數名都是這樣.
如果是C++的話, 這段代碼該怎麼寫?
下面是C++的代碼
上述兩端代碼非常相似, 只有一個不同點
C代碼: (*env)->GetStringUTFChars(env, string, 0);
C++代碼: env->GetStringUTFChars(string, 0);
C語言中使用的是結構體的函數指針, 而在C++中使用的還是struct, 我們知道struct在C++中和class的功能是幾乎一樣的, struct也可以用來定義類, 所以env在C++中是個類對象的指針.
編譯和運行
這裏使用的是微軟編譯器, 編譯C語言版的dll
>cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD Sample1.c -FeSample1.dll
編譯C++版本的dll
>cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD Sample1.cpp -FeSample1.dll
運行
>java Sample1
注意: 64位版本的JDK可能會在運行時報錯:
java.lang.UnsatisfiedLinkError: ...Sample1.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
如果您有這樣的錯誤, 請使用32位的JDK來重新運行.
運行結果如下:
intMethod:
25 booleanMethod:
false stringMethod: JAVA intArrayMethod:
36 |
源代碼下載: Sample1.zip
運行其中的build&run.bat文件即可, 如有錯誤請根據實際情況修改其中的一些參數.
DLL工程文件VC6.0和VS2010的: VC6.0&VS2010.zip
參考文獻:
- Scott Stricker, 用 JNI 進行 Java 編程, http://www.ibm.com/developerworks/cn/education/java/j-jni/section2.html
- JDK 6u30 docs, Java Native Interface Specification, Chapter 3 JNI Types and Data Structures, Type Signatures.