JNI之C++調用Java類 ——java.lang.String

JNIC++調用Java

              ——java.lang.String

              爲什麼要用C++調用Java類?很難回答,寫着文章只是覺得JNI很有意思。於是開始編寫一段使用VC++Windows系統裏調用javaString類,在C++裏調用String類內的一些方法。

JNI已經被開發了很多年,而在我2年多的Java編程時間裏從來沒有接觸過。直到最近研究JVM實現原理才注意到JNI JNIJava Native InterfaceNative這個詞我見過我認爲最恰當的翻譯就是原生。原生的意思就是來自系統自己的,原汁原味的東西,例如Win32 APIJava類需要在虛擬機上運行,也就不是原生的,同樣.NET Framework也不是原生的。JNI也就是Java原生接口。關於JNI的規範,以及爲什麼要使用它,它能做些什麼,都在http://java.sun.com/j2se/1.4.2/docs/guide/jni/spec/jniTOC.html裏記述着。

              JNI是規範,它規定了虛擬機的接口,而把具體的實現留給開發者。

JVM的實現不是唯一的,目前存在很多種Java虛擬機,Sun HotspotIBM JDK,還有HP的,Kaffe等等。最流行的就是SunHotspot,最複雜的就是IBM JDK,這是IBM的一貫作風。本文不討論JVM的實現,只關注JNI。如果您安裝了SunJDK,您就能在[JAVA_HOME]/include目錄下找到jni.h。這個頭文件就是虛擬機的唯一接口,你可以調用它聲明的函數創建一個JVM

              在說明C++調用Java類之前,我想先演示一下如果編寫Java Native Method

1.編寫帶有Native方法的Java

package org.colimas.jni.test;

 

public class JniTest {

 

               static { System.loadLibrary("JniTestImpl"); }  //JVM調用JniTestImpl.dll

               

               public JniTest(){

               

               }

               //原生方法

               public native void print(String str);

              /**

               * @param args

               */

              public static void main(String[] args) {

                           

                            JniTest test=new JniTest();

                            test.print("hello JVM"); //調用原生方法

                           

              }

}

2.使用javah生成c語言頭文件。

javah -jni org.colimas.jni.test.JniTest

目錄裏多了一個org_colimas_jni_test_JniTest.h文件,打開文件,內容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class org_colimas_jni_test_JniTest */

 

#ifndef _Included_org_colimas_jni_test_JniTest

#define _Included_org_colimas_jni_test_JniTest

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     org_colimas_jni_test_JniTest

 * Method:    print

 * Signature: (Ljava/lang/String;)V

 */

JNIEXPORT void JNICALL Java_org_colimas_jni_test_JniTest_print

  (JNIEnv *, jobject, jstring);

 

#ifdef __cplusplus

}

#endif

#endif

 

其中的Java_org_colimas_jni_test_JniTest_print就是JniTest類裏面的print原生方法的C語言聲明。

3.編寫C代碼實現原生方法print

#include <jni.h>

#include "org_colimas_jni_test_JniTest.h" //javah生成的頭文件

#include <stdio.h>

 

JNIEXPORT void JNICALL Java_org_colimas_jni_test_JniTest_print

  (JNIEnv *env, jobject object,jstring str)

{

       //獲得字符串

       const char * txt=(*env)->GetStringUTFChars(env,str,0);

       printf("%s/n",txt); //打印到控制檯

       return;

}

 

參數JNIEnv *envJNI裏最重要的變量。Java.exe創建JVM,之後JVM生成一個env,該env相當於JVM內的Session,可以完成創建Java對象,調用類方法,獲得類的屬性等等。

在這裏env將方法的參數StrJNIjstring類型轉換爲常數char數組。

4.編譯

cl  /Ic:/j2sdk1.4.2_10/include /Ic:/j2sdk1.4.2_10/include/win32 /c  JniTestImpl.c

 

5.連接爲DLL

link /dll JniTestImpl.obj

6.設置PATH

set PATH=C:/MyProject/Colimas/CD/JNI/MyJNI;%PATH%

7.運行

java org.colimas.jni.test.JniTest

返回結果

hello JVM

結束

              以上是實現Java原生方法的開發過程,下面進入正題,使用C++調用Javajava.lang.String類。

1.      Object類出創建JVM

使用Java類之前必須要創建JVM環境。JDKjava.exe來完成。本文有Object類的靜態方法BeginJVM來創建,用EndJVM來關閉。

創建JVM之後會在創建2個變量,分別是JNIEnv* envJavaVM* jvmJNIEnv上文已經說明,JavaVM,顧名思義,代表Java虛擬機,用它來關閉JVM

Object類的頭文件

 

#include "jni.h"

class Object 

{

public:

              static bool BeginJVM();

              static bool EndJVM();

              Object();

              virtual ~Object();

 

protected:

               static JNIEnv* env;

               static JavaVM* jvm;

};

 

object.cpp代碼

#include "stdafx.h"

#include "JavaClasses.h"

#include "Object.h"

 

Object::Object()

{}

 

Object::~Object()

{}

 

JNIEnv* Object::env=NULL;

JavaVM* Object::jvm=NULL;

//創建JVM

bool Object::BeginJVM()

{

              JavaVMOption options[3];

              JavaVMInitArgs vm_args;

//各種參數

              options[0].optionString="-Xmx128m";

              options[1].optionString="-Verbose:gc";

              options[2].optionString="-Djava.class.path=.";

 

              vm_args.version=JNI_VERSION_1_2;

              vm_args.options=options;

              vm_args.nOptions=3;

//創建JVM,獲得jvmenv

              int res = JNI_CreateJavaVM(&jvm,(void **)&env, &vm_args);

              return true;

}

 

bool Object::EndJVM()

{

//關閉JVM

              jvm->DestroyJavaVM();

              return true;

}

2.      C++String類調用java.lang.String類方法

編寫C++版的String類,調用java String類方法。調用的方法如下:

              String  replaceAll(String regex, String replacement);

              boolean endsWith(String str);

              int indexOf(String str);

              int compareTo(String anotherString);

              char charAt(int i);

 

String的頭文件:

class String  :public Object

{

public:

//與要調用的Java方法名一致。

              const char * replaceAll(char *regex,char *replacement);

              bool endsWith(char * str);

              int indexOf(char * str);

              int compareTo(char *anotherString);

              char charAt(int i);

              String(char *str);

              virtual ~String();

 

};

 

實現:

 

#include "stdafx.h"

#include "String.h"

#include "jni.h"

 

using namespace std;

jclass clazz;    //全局變量,用來傳遞class

jobject object;  //全局變量,用來傳遞object

String::String(char *str)

{

    jstring jstr;

 

              if (Object::env ==NULL)

              {

                            cout << "JVM is not created" << endl;

                            exit(-1);

              }

              //獲得java.lang.String

              clazz=Object::env->FindClass("java/lang/String");

              if (clazz ==0 ){

                            cout << "Class is not found" << endl;

                            exit(-1);

              }

              //獲得String(String str)構造體

              jmethodID mid= Object::env->GetMethodID(clazz,"<init>", "(Ljava/lang/String;)V");

              if (mid==0){

                            cerr<< "GetMethodID Error for class" << endl;

                            exit(-1);

              }

//江字符串封裝爲jstring

    jstr = Object::env->NewStringUTF(str);

    if (jstr == 0) {

                            cerr << "Out of memory" <<endl;

        exit(-1);

    }

 

              cout << "invoking method" << endl;

//創建一個java.lang.String對象。

              object=Object::env->NewObject(clazz,mid,jstr);

 

}

 

String::~String()

{}

 

char String::charAt(int i)

{

              if (Object::env ==NULL)

              {

                            cout << "JVM is not created" << endl;

                            exit(-1);

              }

              if (clazz ==0 ){

                            cout << "Class is not found" << endl;

                            exit(-1);

              }

              if (object ==0 ){

                            cout << "String object is not created" << endl;

                            exit(-1);

              }

              jmethodID mid;

              //獲得charAt方法,(IC表示 參數爲int型,返回char型。詳細參見JNI規範

              mid= Object::env->GetMethodID(clazz,"charAt", "(I)C");

              if (mid==0){

                            cerr<< "GetMethodID Error for class" << endl;

                            exit(-1);

              }

              jint ji=i;

              cout << "invoking method" << endl;

//調用charAt

              jchar z=Object::env->CallCharMethod(object,mid,i);

//返回結果。

              return z;

 

}

 

int String::compareTo(char *anotherString)

{

              if (Object::env ==NULL)

              {

                            cout << "JVM is not created" << endl;

                            exit(-1);

              }

              if (clazz ==0 ){

                            cout << "Class is not found" << endl;

                            exit(-1);

              }

              if (object ==0 ){

                            cout << "String object is not created" << endl;

                            exit(-1);

              }

              jmethodID mid;

//(Ljava/lang/String;)I表示參數爲java.lang.String,返回int

              mid= Object::env->GetMethodID(clazz,"compareTo", "(Ljava/lang/String;)I");

              if (mid==0){

                            cerr<< "GetMethodID Error for class" << endl;

                            exit(-1);

              }

              jstring jstr = Object::env->NewStringUTF(anotherString);

              cout << "invoking method" << endl;

//調用方法

jint z=Object::env->CallIntMethod(object,mid,jstr);

//返回結果

              return z;

}

 

int String::indexOf(char *str)

{

              if (Object::env ==NULL)

              {

                            cout << "JVM is not created" << endl;

                            exit(-1);

              }

              if (clazz ==0 ){

                            cout << "Class is not found" << endl;

                            exit(-1);

              }

              if (object ==0 ){

                            cout << "String object is not created" << endl;

                            exit(-1);

              }

              jmethodID mid;

 

              mid= Object::env->GetMethodID(clazz,"indexOf", "(Ljava/lang/String;)I");

              if (mid==0){

                            cerr<< "GetMethodID Error for class" << endl;

                            exit(-1);

              }

              jstring jstr = Object::env->NewStringUTF(str);

              cout << "invoking method" << endl;

              jint z=Object::env->CallIntMethod(object,mid,jstr);

 

              return z;

}

 

bool String::endsWith(char *str)

{

              if (Object::env ==NULL)

              {

                            cout << "JVM is not created" << endl;

                            exit(-1);

              }

              if (clazz ==0 ){

                            cout << "Class is not found" << endl;

                            exit(-1);

              }

              if (object ==0 ){

                            cout << "String object is not created" << endl;

                            exit(-1);

              }

              jmethodID mid;

 

              mid= Object::env->GetMethodID(clazz,"endsWith", "(Ljava/lang/String;)Z");

              if (mid==0){

                            cerr<< "GetMethodID Error for class" << endl;

                            exit(-1);

              }

              jstring jstr = Object::env->NewStringUTF(str);

              cout << "invoking method" << endl;

              bool z=Object::env->CallBooleanMethod(object,mid,jstr);

             

              return z;

}

 

const char * String::replaceAll(char *regex, char *replacement)

{

 

 

              if (Object::env ==NULL)

              {

                            cout << "JVM is not created" << endl;

                            exit(-1);

              }

              if (clazz ==0 ){

                            cout << "Class is not found" << endl;

                            exit(-1);

              }

              if (object ==0 ){

                            cout << "String object is not created" << endl;

                            exit(-1);

              }

              jmethodID mid;

 

              mid= Object::env->GetMethodID(clazz,"replaceAll", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");

              if (mid==0){

                            cerr<< "GetMethodID Error for class" << endl;

                            exit(-1);

              }

              jvalue array[2];

              jstring jreg = Object::env->NewStringUTF(regex);

              jstring jstr = Object::env->NewStringUTF(replacement);

              array[0].l=jreg;

              array[1].l=jstr;

              cout << "invoking method" << endl;

//傳入參數,調用replaceAll方法

              jobject z=Object::env->CallObjectMethodA(object,mid,array);

              const char *result=Object::env->GetStringUTFChars((jstring)z, 0);

              return (const char *)result;

}

 

3.測試

 

編寫測試代碼

using namespace std;

 

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

              int nRetCode = 0;

 

              if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))

              {

                            cerr << _T("Fatal Error: MFC initialization failed") << endl;

                            nRetCode = 1;

              }

              else

              {

//創建JVM

                            Object::BeginJVM();

                           

                            String test("hello");

                            //調用replaceAll

                            const char *result=test.replaceAll("l","z");

                            //返回結果

                            cout<< result <<endl;

                            //關閉JVM

                            Object::EndJVM();

              }

 

              return nRetCode;

}

 

 

 

4.運行

編譯需要 jni.hjvm.lib文件。

jni.h[JAVA_HOME]/include

jvm.lib[JAVA_HOME]/lib

 

運行需要jvm.dll

jvm.dll[JAVA_HOME]/ jre/bin/client

運行結果如下:

invoking method

invoking method

hezzo

Press any key to continue

             

              儘管本文的代碼很有意思,但我還沒有想到有什麼價值,以及應用到實際項目中的理由。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章