前言

简单介绍C/C++如何调用Java,内容适合未接触过此类工程的朋友作为上手参考。


一、编译环境

1. 64位的win10系统

2. JDK:jdk-8u181-windows-x64.exe

3. IDE VS2017

二、调用步骤及Java虚拟机使用方法

一般步骤:

  1. 编写Java代码, 并编译
  2. 编写C/C++代码
  3. 配置lib进行编译,配置PATH添加相应的dll

使用方法:

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

三、编译过程

1、配置环境变量


添加环境变量JAVA_HOME,路径为Java安装路径

在path里面添加路径,路径为bin目录所在路径和jvm.dll所在路径

2、构建被调用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

可以在当前目录下看到Sample2.class文件, 编译成功.。

可以查看Sample2类中的签名

javap -s -private Sample2

结果如下

Compiled from "Sample2.java"
public class Sample2 {public java.lang.String name;descriptor: Ljava/lang/String;public Sample2();descriptor: ()Vpublic static java.lang.String sayHello(java.lang.String);descriptor: (Ljava/lang/String;)Ljava/lang/String;public java.lang.String sayHello();descriptor: ()Ljava/lang/String;
}

3、创建C/C++调用Java工程(VS控制台工程x64)

c++示例代码如下:

#include <jni.h>
#include <string.h>
#include <stdio.h>
// 环境变量PATH在windows下和linux下的分割符定义
#ifdef _WIN32
#define PATH_SEPARATOR ';'
#else
#define PATH_SEPARATOR ':'
#endifint 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=D:/c_call_java/javaclass/";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("Sample2");if (cls != 0){// 获取方法ID, 通过方法名和签名, 调用静态方法mid = env->GetStaticMethodID( cls, "sayHello", "(Ljava/lang/String;)Ljava/lang/String;");if (mid != 0){const char* name = "World";jstring arg = env->NewStringUTF(name);jstring result = (jstring)env->CallStaticObjectMethod(cls, mid, arg);const char* str = env->GetStringUTFChars(result, 0);printf("Result of sayHello: %s\n", str);env->ReleaseStringUTFChars(result, 0);}/*** 新建一个对象 ***/// 调用默认构造函数//obj = (*env)->AllocObject(env, cls);// 调用指定的构造函数, 构造函数的名字叫做<init>mid = env->GetMethodID( cls, "<init>", "()V");obj = env->NewObject( cls, mid);if (obj == 0){printf("Create object failed!\n");}/*** 新建一个对象 ***/// 获取属性ID, 通过属性名和签名fid = (env)->GetFieldID( cls, "name", "Ljava/lang/String;");if (fid != 0){const char* name = "icejoywoo";jstring arg = (env)->NewStringUTF( name);(env)->SetObjectField( obj, fid, arg); // 修改属性}// 调用成员方法mid = (env)->GetMethodID( cls, "sayHello", "()Ljava/lang/String;");if (mid != 0){jstring result = (jstring)(env)->CallObjectMethod( obj, mid);const char* str = (env)->GetStringUTFChars(result, 0);printf("Result of sayHello: %s\n", str);(env)->ReleaseStringUTFChars( result, 0);}}(jvm)->DestroyJavaVM();return 0;}else{printf("JVM Created failed!\n");return -1;}
}

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

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

工程相关配置如下所示:
添加包含目录

添加库目录

引用库文件jvm.lib

运行结果:

Result of sayHello: Hello, World!
Result of sayHello: Hello, icejoywoo!

4、虚拟的创建

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

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

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

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

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

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

下面就是创建虚拟机

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=."看着眼熟吧,这个就是传入当前路径,作为JVM寻找class的用户自定义路径(当然也可以不在当前路径,你可以随便修改)。

vm_args.version是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

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

vm_args.options = options把JavaVMOption传给JavaVMInitArgs里面去.

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

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

#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对象比较简单,FindClass(env, className).

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

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

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

调用默认构造函数

// 调用默认构造函数

obj = env->AllocObject(cls);

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

// 调用指定的构造函数, 构造函数的名字叫做

mid = env->GetMethodID(cls, "<init>", "()V");
obj = env->NewObject(cls, mid);

调用方法和修改属性

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

jmethodID mid;
jfieldID fid;

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

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

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

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

方法的调用如下

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

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

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

获取属性ID

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

修改属性的值

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

关于jstring的说明

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

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

jstring arg = env->NewStringUTF(name);

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

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

四、总结

编译和运行

  1. 编译需要头文件, 头文件在这两个目录中%JAVA_HOME%\include和%JAVA_HOME%\include\win32, 第一个是与平台无关的, 第二个是与平台有关的, 由于笔者的系统是windows, 所以是win32.
  2. 编译的时候还要一个lib文件(jvm.lib), 是对虚拟机的支持, 保证编译通过.
  3. 我们可以看到在当前目录下Sample2.exe, 运行的时候需要jvm.dll(不要将其复制到当前目录下, 这样不可以运行, 会导致jvm创建失败)
  4. jvm.dll在%JAVA_HOME%\jre\bin\server\目录下, 所以我把这个目录加入到PATH中, 然后就可以运行

五、注意

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

C/C++如何调用Java相关推荐

  1. air调用java,AIR2.0入门教程:与Java应用交互

    在之前的一篇文章中,我介绍了如何使用AIR2.0新增的NativeProcess类与本地进程进行交互和通讯,在那个例子里面我们使用了C++ 的代码,实际上只要是基于命令行的标准输入输出,AIR2.0的 ...

  2. matlab中调用java代码_Matlab中调用第三方Java代码

    在Java中采用Matlab JA Builder可以实现调用m文件,采用这样的方式,可在Matlab的M文件中,直接调用Java类.这种方式可以表示为Java--> Matlab( m, Ja ...

  3. .NET调用JAVA的WebService方法

    调用WebService,最简单的办法当然是直接添加WEB引用,然后自动产生代理类,但是在调用JAVA的WebService时并没有这么简单,特别是对于SoapHeader的处理,在网上也有相关资料, ...

  4. loadrunner 调用java_LoadRunner调用Java程序—性能测试

    为了充分利用LoadRunner的场景控制和分析器,帮助我们更好地控制脚本加载过程,从而展现更直观有效的场景分析图表.本次将重点讨论LoadRunner如何调用Java测试代码,完成压力测试. 通常我 ...

  5. ruby调用java代码

    为什么80%的码农都做不了架构师?>>>    ruby使用rjb调用java代码 require 'rjb'#加载jar包 Rjb::load(classpath = '/home ...

  6. c#调用java开发的webservice_用C#.NET调用Java开发的WebService传递int,double问题

    用C#.NET调用Java开发的WebService时,先在客户端封装的带有int属性的对象,当将该对象传到服务器端时,服务器端可以得到string类型的属性值,却不能得到int类型.double和D ...

  7. 调用java_UiPath如何调用Java

    调用Java方法(Invoke Java Method)的介绍 从Java Scope中的.jar加载的方法中调用指定的Java方法.并结果存储在变量中 二.Invoke Java Method 在U ...

  8. 基于 Android NDK 的学习之旅----- C调用Java

    2019独角兽企业重金招聘Python工程师标准>>> 基于 Android NDK 的学习之旅----- C调用Java 许多成熟的C引擎要移植到Android 平台上使用 , 一 ...

  9. android 调用java类_Android中在WebView里实现Javascript调用Java类的方法

    搜索热词 为了方便网页和Android应用的交互,Android系统提供了WebView中JavaScript网页脚本调用Java类方法的机制.只要调用addJavascriptInterface方法 ...

  10. 在R中调用Java代码

    我们都知道Java语言长期处于霸主地位,在所有编程语言排行榜中,Java也是常年位居第一.在近几年也是爆炸式发展,几乎覆盖到了应用开发的所有领域.而R语言则在统计圈和数据可视化处于佼佼者,如果将Jav ...

最新文章

  1. 【安全系列之跨域】跨域解决方案
  2. mysql 命令行批量sql_命令行中执行批量SQL的方法
  3. toughradius 配置mysql_ToughRADIUS 安装进阶篇
  4. Windows用WinDbg分析蓝屏dump文件查找原因(转)
  5. ajax请求携带tooken_9 HTMLJS等前端知识系列之Ajax post请求带有token向Django请求
  6. c语言学习-输入一个十进制数,输出其对应的八进制数据
  7. Python实现基于HDFS的云盘系统
  8. CRT(C Runtime Library)—— C/C++运行时库
  9. 《图解算法》学习笔记之散列表(hash table)
  10. Hadoop大数据技术原理与应用-第一章初识Hadoop
  11. LED显示屏智能化监控运维管理解决方案
  12. Resin服务器配置指南
  13. html中版权怎么写,html版权符号代码怎么打?通过什么方式输入?
  14. xd使用技巧_Adobe XD —你不知道的30条提示和技巧!
  15. 2分钟入侵网站全程实录
  16. 陈天桥的大脑在孕育什么新传奇(转)
  17. 1078 字母三角形
  18. MAX6951EEE+T LED显示驱动器MAXIM 共阴极显示驱动器
  19. 年轻人,请听我说……
  20. 如何在中关村做好一门小生意——北漂18年(18)

热门文章

  1. windows 10(64位) 本地模式安装Hadoop和Hbase
  2. 中国十大计算机学院排名2015,中国计算机学院排名
  3. 产品经理vs项目经理?四类PM区别都在这里啦
  4. Irvue for Mac(苹果壁纸软件)
  5. 网吧服务器系统是怎么弄的,网吧无盘服务器系统的安装及设置
  6. Spark History Server 没有生效
  7. 【Zoomit】的安装及使用方法
  8. Java课程学习四:编程题
  9. excel删除无尽空白行_从0到1快速入门Excel透视表,看这一篇就够了
  10. JLabel.setBounds的四个参数