JNI之C++调用Java类 —— java.lang.String

JNI之C++调用Java类 —— java.lang.String

    为什么要用C++调用Java类?很难回答,写着文章只是觉得JNI很有意思。于是开始编写一段使用VC++在Windows系统里调用java的String类,在C++里调用String类内的一些方法。

    JNI已经被开发了很多年,而在我2年多的Java编程时间里从来没有接触过。直到最近研究JVM实现原理才注意到JNI。 JNI既Java Native Interface,Native这个词我见过我认为最恰当的翻译就是原生。原生的意思就是来自系统自己的,原汁原味的东西,例如Win32 API。Java类需要在虚拟机上运行,也就不是原生的,同样.NET Framework也不是原生的。JNI也就是Java原生接口。关于JNI的规范,以及为什么要使用它,它能做些什么,都在http://java.sun.com/j2se/1.4.2/docs/guide/jni/spec/jniTOC.html里记述着。

    JNI是规范,它规定了虚拟机的接口,而把具体的实现留给开发者。

    JVM的实现不是唯一的,目前存在很多种Java虚拟机,Sun Hotspot,IBM JDK,还有HP的,Kaffe等等。最流行的就是Sun的Hotspot,最复杂的就是IBM JDK,这是IBM的一贯作风。本文不讨论JVM的实现,只关注JNI。如果您安装了Sun的JDK,您就能在[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 ",txt); //打印到控制台
       return;
}

参数JNIEnv *env,是JNI里最重要的变量。Java.exe创建JVM,之后JVM生成一个env,该env相当于JVM内的Session,可以完成创建Java对象,调用类方法,获得类的属性等等。

在这里env将方法的参数Str从JNI的jstring类型转换为常数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++调用Java的java.lang.String类。

1. Object类出创建JVM。

使用Java类之前必须要创建JVM环境。JDK由java.exe来完成。本文有Object类的静态方法BeginJVM来创建,用EndJVM来关闭。

创建JVM之后会在创建2个变量,分别是JNIEnv* env和JavaVM* jvm,JNIEnv上文已经说明,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,获得jvm和env
    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方法,(I)C表示 参数为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.h和jvm.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
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值