C++调用Java接口
一、配置Java环境
安装jdk,我这里使用jdk1.8 32位版本,下载地址:https://www.oracle.com/java/technologies/downloads/#java8-windows
下载安装后,设置环境变量:
JAVA_HOME
C:\Program Files (x86)\Java\jdk-1.8
设置Path:
新增三个:
%JAVA_HOME%\bin
%JAVA_HOME%\jre\bin
%JAVA_HOME%\jre\bin\client
二、创建Java类
打开IDEA创建一个工程:例如JavaDemo
创建类:MyJavaClass
public class MyJavaClass {// 成员方法1,带参数,有返回值public String getSomething(String str){return str + " hello";}// 成员方法2,带参数,有返回值public int addNumber(int a1, int a2){return a1 + a2;}// 成员方法3,带参数,没有返回值public void printSomething(String str){System.console().printf(str);}// 静态方法,带参数,有返回值public static String staticMethodExample(String str){return str + " static method hello";}
}
在Main.java
添加一些测试代码验证是否有问题:(为了避免MyJavaClass有问题,在这里调用一下MyJavaClass里的方法测试一下)
public class Main {public static void main(String[] args) {MyJavaClass myJavaClass = new MyJavaClass();String ret = myJavaClass.getSomething("Java");System.out.println("myJavaClass.getSomething return: " + ret);int num = myJavaClass.addNumber(1, 5);System.out.println("myJavaClass.addNumber return: " + num);myJavaClass.printSomething("Java");String ret2 = MyJavaClass.staticMethodExample("Java");System.out.println("MyJavaClass.staticMethodExample return: " + ret2);}
}
运行,得到结果:
三、生成jar
在项目中右键,选择Open Module Settings
选择Artifacts
最后,生成jar,在菜单栏选择 Build -> Build Artifacts
在菜单栏选择 Build -> Build Artifacts
后,在IDE的界面中随机在某个地方会出现下面的Build Artifact
的弹窗,点击“Build
”
Build完成后,就会在 out/artifacts
目录下生成jar文件
注意:如果JaveDemo引用了其他第三方库,也是会被一起打包到jar里面的
四、C++调Java接口示例
使用VS创建一个C++工程
属性设置:C/C++ -> General -> Additional Include Directories
添加:
$(JAVA_HOME)\include
$(JAVA_HOME)\include\win32
Linker -> General -> Additional Library Directories
添加:
$(JAVA_HOME)\lib
Linker -> General -> Additional Library Directories
添加: jvm.lib
创建一个main.cpp
,添加代码:
#include <iostream>#include "jni.h"int main() {std::string jarFile = "../../JavaDemo/out/artifacts/JavaDemo_jar/JavaDemo.jar";std::string optionString = "-Djava.class.path=" + jarFile;JavaVMOption options[1];options[0].optionString = const_cast<char*>(optionString.c_str());JavaVMInitArgs vm_args;memset(&vm_args, 0, sizeof(vm_args));vm_args.version = JNI_VERSION_1_8;vm_args.nOptions = 1;vm_args.options = options;vm_args.ignoreUnrecognized = false;JavaVM* jvm = nullptr;JNIEnv* env = nullptr;// 启动虚拟机long status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);if (status == JNI_ERR) {std::cout << "JNI_CreateJavaVM失败," << status << std::endl;return -1;}// 先获得class对象jclass cls = env->FindClass("MyJavaClass"); // 如果类带包名,这里需要加上包名,比如 com/example/MyJavaClass,把包名的.换成/if (cls == nullptr) {std::cout << "FindClass MyJavaClass 失败" << std::endl;return -1;}// 下面是调用成员方法和静态方法的示例,为了区分开来演示,用{}分开在两个作用域内。工作代码中不需要这样做。// 调用成员方法{// 查找构造函数并创建对象jmethodID constructor = env->GetMethodID(cls, "<init>", "()V");if (constructor == nullptr) {std::cout << "查找构造函数失败" << std::endl;return -1;}jobject myJavaClassObj = env->NewObject(cls, constructor);if (myJavaClassObj == nullptr) {std::cout << "MyJavaClass类对象创建失败" << std::endl;jvm->DestroyJavaVM(); // 后面的代码只要退出应该都需要先释放VMreturn -1;}// 一、调用成员方法 getSomething 的示例{// 1. 获取成员方法getSomething的IDjmethodID getSomethingMethodid = env->GetMethodID(cls, "getSomething", "(Ljava/lang/String;)Ljava/lang/String;");if (getSomethingMethodid == nullptr) {std::cout << "getSomethingMethodid获取失败" << std::endl;return -1;}// 2. 调用成员方法getSomethingjstring inputString = env->NewStringUTF("Test"); // 这是成员方法getSomething的参数jstring ret = (jstring)env->CallObjectMethod(myJavaClassObj, getSomethingMethodid, inputString);const char* str = env->GetStringUTFChars(ret, 0);std::cout << "成员方法getSomething返回:" << str << std::endl;env->ReleaseStringUTFChars(ret, 0);}// 二、调用成员方法 addNumber 的示例{// 1. 获取成员方法getSomething的IDjmethodID addNumberMethodid = env->GetMethodID(cls, "addNumber", "(II)I");if (addNumberMethodid == nullptr) {std::cout << "addNumberMethodid获取失败" << std::endl;return -1;}// 2. 调用成员方法getSomethingjint param1 = 1;jint param2 = 5;jint ret = (jint)env->CallObjectMethod(myJavaClassObj, addNumberMethodid, param1, param2);std::cout << "成员方法getSomething返回:" << ret << std::endl;}}// 调用静态方法{jmethodID methodId = env->GetStaticMethodID(cls, "staticMethodExample", "(Ljava/lang/String;)Ljava/lang/String;");if (methodId == nullptr) {std::cout << "获取静态方法staticMethodExample的id失败" << std::endl;return -1;}jstring inputString = env->NewStringUTF("Test"); // 这是静态方法staticMethodExample的参数jstring ret = (jstring)env->CallStaticObjectMethod(cls, methodId, inputString);const char* str = env->GetStringUTFChars(ret, 0);std::cout << "静态方法staticMethodExample返回:" << str << std::endl;env->ReleaseStringUTFChars(ret, 0);}// 释放jvmif (jvm){jvm->DestroyJavaVM();}return 0;
}
代码运行结果:
其中:
- 调用Java的成员函数,需要
先创建对象
,获取成员函数的MethodID,然后再调用成员函数; - 调用Java的静态函数,
不需要创建对象
,直接获取静态方法的MethodID,再调用即可; - GetMethodID和GetStaticMethodID获取方法ID时,需要填入
方法名
和方法签名
,具体转换如下表:
(1)基础类型:
Java Type | Native Type | Signature |
---|---|---|
byte | jbyte | B |
char | jchar | C |
double | jdouble | D |
float | jfloat | F |
int | jint | I |
short | jshort | S |
long | jlong | J |
boolean | jboolean | Z |
void | void | V |
(2)引用数据类型:
Java Type | Native Type | Signature |
---|---|---|
所有对象 | jobject | L+classname +; |
Class | jclass | Ljava/lang/Class; |
String | jstring | Ljava/lang/String; |
Throwable | jthrowable | Ljava/lang/Throwable; |
Object[] | jobjectArray | [L+classname +; |
byte[] | jbyteArray | [B |
char[] | jcharArray | [C |
double[] | jdoubleArray | [D |
float[] | jfloatArray | [F |
int[] | jintArray | [I |
short[] | jshortArray | [S |
long[] | jlongArray | [J |
boolean[] | jbooleanArray | [Z |
-
方法签名组装方式:
(参数类型1参数类型2…)返回值类型
例如,(II)I 表示接受两个整数参数,返回一个整数。下面是一些方法签名的具体示例:
int add(int a, int b) => (II)I
String concat(String str1, String str2) => (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
void printMessage(String message) => (Ljava/lang/String;)V
boolean isValid(int number) => (I)Z还可以通过
javap -s -p
指令获取对应的签名信息,例如,我在MyJavaClass.class生成的目录,执行:javap -s -p MyJavaClass
-
更多参考:https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html
五、问题
- 下图是环境变量的问题,按照第一部分设置环境变量后,重启VS再试
- 32位与64位jdk不匹配的问题,如果C++程序是32位,jdk也要是32位,如果C++程序是64位,则jdk也需要为64位
六、其他
注意:开发环境可以安装完整的jdk,但是打包在其他电脑上运行,运行环境就只需要安装jre就可以
运行环境设置:
jre下载地址:https://www.oracle.com/java/technologies/downloads/#jre8-windows
设置环境变量Path:
C:\Program Files (x86)\ava\latest\jre-1.8\bin
C:\Program Files (x86)\ava\latest\jre-1.8\bin\client
七、代码
https://gitee.com/jie-xio/cpp_samples/tree/master/CppCallJavaDemo