在c/c++中调用Java方法

在c/c++中调用Java方法

JNI就是Java在c/c++中调用Java方法,既可以实现Java调用本地库, 也可以实现C/C++调用Java代码, 从而实现了两种语言的互通,可以让我们更加灵活的使用。

通过使用JNI可以从一个侧面了解Java内部的一些实现.

本文使用的环境是

64位的win7系统JDK 1.6.0u30 (32位)C/C++编译器是 (R) 32-bit C/C++ 12.00.8168 for 80×86 (VC 6.0的, 其他版本的也可以编译通过, 测试过)

本文使用到的一些功能

创建虚拟机寻找class对象, 创建对象调用静态方法和成员方法获取成员属性, 修改成员属性

C/C++调用Java代码的一般步骤

编写Java代码, 并编译编写C/C++代码配置lib进行编译,配置PATH添加相应的dll或so并运行

编写Java代码并编译

这段代码非常简单, 有个静态方法和成员方法, 一个的成员变量

public class Sample2 {
    public String name;
    public static String sayHello(String name) {
        return "Hello, " + name + "!";
    }
    public String sayHello() {
        return "Hello, " + name + "!";
    }
}

由于没有定义构造函数, 所以会有一个默认的构造函数.

运行下面的命令编译

javac Sample2.java

可以在当前目录下看到.class文件, 编译成功, 第一步完成了, So easy!

可以查看类中的签名

javap -s -private Sample2

结果如下

Compiled from "Sample2.java"
public class Sample2 extends java.lang.Object{
public java.lang.String name;
  Signature: Ljava/lang/String;
public Sample2();
  Signature: ()V
public static java.lang.String sayHello(java.lang.String);
  Signature: (Ljava/lang/String;)Ljava/lang/String;
public java.lang.String sayHello();
  Signature: ()Ljava/lang/String;
}

关于签名的含义, 请参看使用JNI进行Java与C/C++语言混合编程(1)–在Java中调用C/C++本地库.

编写C/C++代码调用Java代码

先贴代码吧

#include 
#include 
#include 
// 环境变量PATH在windows下和linux下的分割符定义
#ifdef _WIN32
#define PATH_SEPARATOR ';'
#else
#define PATH_SEPARATOR ':'
#endif
int main(void)
{
    JavaVMOption options[1];
    JNIEnv *env;
    JavaVM *jvm;
    JavaVMInitArgs vm_args;
    long status;
    jclass cls;
    jmethodID mid;
    jfieldID fid;
    jobject obj;
    options[0].optionString = "-Djava.class.path=.";
    memset(&vm_args, 0, sizeof(vm_args));
    vm_args.version = JNI_VERSION_1_4;
    vm_args.nOptions = 1;
    vm_args.options = options;
    // 启动虚拟机
    status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    if (status != JNI_ERR)
    {
        // 先获得class对象
        cls = (*env)->FindClass(env, "Sample2");
        if (cls != 0)
        {
            // 获取方法ID, 通过方法名和签名, 调用静态方法
            mid = (*env)->GetStaticMethodID(env, cls, "sayHello", "(Ljava/lang/String;)Ljava/lang/String;");
            if (mid != 0)
            {
                const char* name = "World";
                jstring arg = (*env)->NewStringUTF(env, name);
                jstring result = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid, arg);
                const char* str = (*env)->GetStringUTFChars(env, result, 0);
                printf("Result of sayHello: %sn", str);
                (*env)->ReleaseStringUTFChars(env, result, 0);
            }
            /*** 新建一个对象 ***/
            // 调用默认构造函数
            //obj = (*env)->AllocObject(env, cls);
            // 调用指定的构造函数, 构造函数的名字叫做
            mid = (*env)->GetMethodID(env, cls, "", "()V");
            obj = (*env)->NewObject(env, cls, mid);
            if (obj == 0)
            {
                printf("Create object failed!n");
            }
            /*** 新建一个对象 ***/
            // 获取属性ID, 通过属性名和签名
            fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
            if (fid != 0)
            {
                const char* name = "icejoywoo";
                jstring arg = (*env)->NewStringUTF(env, name);
                (*env)->SetObjectField(env, obj, fid, arg); // 修改属性
            }
            // 调用成员方法
            mid = (*env)->GetMethodID(env, cls, "sayHello", "()Ljava/lang/String;");
            if (mid != 0)
            {
                jstring result = (jstring)(*env)->CallObjectMethod(env, obj, mid);
                const char* str = (*env)->GetStringUTFChars(env, result, 0);
                printf("Result of sayHello: %sn", str);
                (*env)->ReleaseStringUTFChars(env, result, 0);
            }
        }
        (*jvm)->DestroyJavaVM(jvm);
        return 0;
    }
    else
    {
        printf("JVM Created failed!n");
        return -1;
    }
}

这段代码大概做了这几件事

创建虚拟机JVM, 在程序结束的时候销毁虚拟机JVM寻找class对象创建class对象的实例调用方法和修改属性虚拟的创建

与之相关的有这样几个变量

JavaVMOption options[1];     
JNIEnv *env;     
JavaVM *jvm;     
JavaVMInitArgs vm_args;

就是我们需要创建的虚拟机实例

相当于在命令行里传入的参数

在Java调用C/C++中每个方法都会有的一个参数, 拥有一个JNI的环境

就是虚拟机创建的初始化参数, 这个参数里面会包含

下面就是创建虚拟机

options[0].optionString = "-Djava.class.path=.";
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_4;
vm_args.nOptions = 1;
vm_args.options = options;
// 启动虚拟机
status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

“-Djava.class.path=.”看着眼熟吧java create,这个就是传入当前路径java create,作为JVM寻找class的用户自定义路径,我们的.class就在当前路径(当然也可以不在当前路径在c/c++中调用Java方法,你可以随便修改)。

.是Java的版本, 这个应该是为了兼容以前的JDK, 可以使用旧版的JDK, 这个宏定义是在jni.h中,  有以下四种

#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
#define JNI_VERSION_1_4 0x00010004
#define JNI_VERSION_1_6 0x00010006

.的含义是, 你传入的有多长, 我们这里就一个, 所以是1.

. = 把传给里面去.

然后就是启动虚拟机了 = (&jvm, (void**)&env, &).

可以通过这个返回值 , 知道虚拟机是否启动成功

#define JNI_OK           0                 /* success */
#define JNI_ERR          (-1)              /* unknown error */
#define JNI_EDETACHED    (-2)              /* thread detached from the VM */
#define JNI_EVERSION     (-3)              /* JNI version error */
#define JNI_ENOMEM       (-4)              /* not enough memory */
#define JNI_EEXIST       (-5)              /* VM already created */
#define JNI_EINVAL       (-6)              /* invalid arguments */

寻找class对象, 并实例化

JVM在Java中都是自己启动的,在C/C++中只能自己来启动了, 启动完之后的事情就和在Java中一样了, 不过要使用C/C++的语法.

获取class对象比较简单java create,(env, ).

cls = (*env)->FindClass(env, "Sample2");

在Java中的类名格式是java.lang., 但是的格式有点不同, 不是使用'.'作为分割, 而是'/', 即java/lang/.

我们知道Java中构造函数有两种, 一种是默认的没有参数的, 一种是自定义的带有参数的. 对应的在C/C++中, 有两种调用构造函数的方法.

调用默认构造函数

// 调用默认构造函数
obj = (*env)->AllocObject(env, cls);

构造函数也是方法, 类似调用方法的方式.

// 调用指定的构造函数, 构造函数的名字叫做
mid = (*env)->GetMethodID(env, cls, "", "()V");
obj = (*env)->NewObject(env, cls, mid);

调用方法和修改属性

关于方法和属性是有两个ID与之对应, 这两个ID用来标识方法和属性.

jmethodID mid;
jfieldID fid;

方法分为静态和非静态的, 所以对应的有

mid = (*env)->GetStaticMethodID(env, cls, "sayHello", "(Ljava/lang/String;)Ljava/lang/String;");
mid = (*env)->GetMethodID(env, cls, "sayHello", "()Ljava/lang/String;");

上面两个方法是同名的, 都叫, 但是签名不同, 所以可以区分两个方法.

JNI的函数都是有一定规律的, 就表示是静态, 没有表示非静态.

方法的调用如下

jstring result = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid, arg);
jstring result = (jstring)(*env)->CallObjectMethod(env, obj, mid);

我们可以看到静态方法是只需要class对象, 不需要实例的, 而非静态方法需要使用我们之前实例化的对象.

属性也有静态和非静态, 示例中只有非静态的.

获取属性ID

fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");

修改属性的值

(*env)->SetObjectField(env, obj, fid, arg); // 修改属性

关于的说明

java的都是使用了, 是双字节的字符, 而C/C++中使用的单字节的字符.

从C转换为java的字符, 使用方法

jstring arg = (*env)->NewStringUTF(env, name);

从java转换为C的字符, 使用

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

编译和运行

编译需要头文件, 头文件在这两个目录中%%和%%\win32, 第一个是与平台无关的, 第二个是与平台有关的, 由于笔者的系统是, 所以是win32.

编译的时候还要一个lib文件, 是对虚拟机的支持, 保证编译通过.

cl -I%% -I%%\win32 .c %%libjvm.lib

我们可以看到在当前目录下.exe, 运行的时候需要jvm.dll(不要将其复制到当前目录下, 这样不可以运行, 会导致jvm创建失败)

set PATH=%%jrebin\;%PATH%

jvm.dll在%%jrebin\目录下, 所以我把这个目录加入到PATH中, 然后就可以运行

of : Hello, World!

of : Hello, !

关于C++的说明

本示例的C++版本, 请自行下载后面的源代码来查看, 区别不是很大.

主要是和两个对象, 在C中是结构体, 是函数指针的集合, 在C++中结构体拥有类的能力, 使用起来更为简便, 与Java之间的差异更小一些.

结语

本文介绍了一个简单的例子, 分析了其中的一些代码, 笔者希望通过这篇文章让大家对JNI的了解更加深入一些.

水平有限, 错漏在所难免, 欢迎指正!

源代码下载: c调用java.zip

使用方法: 参照里面的build&run.bat, 使用了%%环境变量.

注意:

动态链接库和JDK都有32位和64位的区别, 使用64位系统的朋友, 要注意这个问题, 可能导致运行或编译错误.还要注意区分C和C++代码, 在JNI中两种代码有一定的区别, 主要是env和jvm两个地方.

公告:
1. 本站所有资源来源于用户上传和网络,如有侵权请联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系站长处理!
6. 本站不售卖代码,资源标价只是站长收集整理的辛苦费!如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
7. 站长QQ号码 2205675299

资源库 - 资源分享下载网 » 在c/c++中调用Java方法

常见问题FAQ

关于资源售价和售后服务的说明?
本站所有资源的标价均为本站收集资源的辛苦费,不代表资源本身的价值。软件是高智慧高价值的商品,不可能是白菜价。本站资源标价只是赞助费用,收取的赞助费仅用来维持本站的日常运营!毕竟收集整理资料需要投入云计算资源和站长大量的精力。
代码有没有售后服务和技术支持?
由于代码的运行具有不可预见性,本站不保证代码完整可运行,不提供技术支持和售后服务。 本站原创代码都是站长自己开发的,可以有偿提供技术支持服务。 网站里标明【亲测】的代码都是站长亲测过的,其他的代码由于精力有限,没有一一测试,不能保证代码就一定能够使用,更没有技术支持服务,下载前请自行斟酌。
有没有搭建服务?
由于搭建服务比较费时费力,所以本站除了原创代码外均不提供搭建服务。本站分享代码纯属兴趣爱好,不以盈利为目的,请勿咨询有没有搭建服务,谢谢理解。
链接地址失效了怎么办?
请带上资源链接地址联系客服,工作时间内我们看到后将第一时间回复。
关于解压密码
本站资源一般都没有加密,如果发现需要解压密码的,那么就输入 hao.35dc.com 试试。

发表评论

资源库,由老程序员细心甄别、精心筛选,只为提供优质的源码资源

关于我们 联系我们