当前位置: 首页 > news >正文

C++调用Java接口

一、配置Java环境

安装jdk,我这里使用jdk1.8 32位版本,下载地址:https://www.oracle.com/java/technologies/downloads/#java8-windows
image.png

下载安装后,设置环境变量:
image.png

JAVA_HOME
C:\Program Files (x86)\Java\jdk-1.8
image.png

设置Path:
image.png
image.png

新增三个:
%JAVA_HOME%\bin
%JAVA_HOME%\jre\bin
%JAVA_HOME%\jre\bin\client
image.png



二、创建Java类

打开IDEA创建一个工程:例如JavaDemo
image.png

创建类:MyJavaClass
image.png

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里的方法测试一下)
image.png

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);}
}

运行,得到结果:
image.png



三、生成jar

在项目中右键,选择Open Module Settings
image.png

选择Artifacts
image.png
image.png
image.png
image.png
image.png
image.png

最后,生成jar,在菜单栏选择 Build -> Build Artifacts
image.png

在菜单栏选择 Build -> Build Artifacts 后,在IDE的界面中随机在某个地方会出现下面的Build Artifact的弹窗,点击“Build
image.png

Build完成后,就会在 out/artifacts 目录下生成jar文件
image.png

注意:如果JaveDemo引用了其他第三方库,也是会被一起打包到jar里面的



四、C++调Java接口示例

使用VS创建一个C++工程
属性设置:C/C++ -> General -> Additional Include Directories 添加:
$(JAVA_HOME)\include
$(JAVA_HOME)\include\win32
image.png

Linker -> General -> Additional Library Directories 添加:
$(JAVA_HOME)\lib
image.png

Linker -> General -> Additional Library Directories 添加: jvm.lib

image.png

创建一个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;
}

代码运行结果:
image.png

其中:

  1. 调用Java的成员函数,需要先创建对象,获取成员函数的MethodID,然后再调用成员函数;
  2. 调用Java的静态函数,不需要创建对象,直接获取静态方法的MethodID,再调用即可;
  3. GetMethodID和GetStaticMethodID获取方法ID时,需要填入方法名方法签名,具体转换如下表:

(1)基础类型:

Java TypeNative TypeSignature
bytejbyteB
charjcharC
doublejdoubleD
floatjfloatF
intjintI
shortjshortS
longjlongJ
booleanjbooleanZ
voidvoidV

(2)引用数据类型:

Java TypeNative TypeSignature
所有对象jobjectL+classname +;
ClassjclassLjava/lang/Class;
StringjstringLjava/lang/String;
ThrowablejthrowableLjava/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. 方法签名组装方式:

    (参数类型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
    image.png

  2. 更多参考:https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html



五、问题

  1. 下图是环境变量的问题,按照第一部分设置环境变量后,重启VS再试

image.png

  1. 32位与64位jdk不匹配的问题,如果C++程序是32位,jdk也要是32位,如果C++程序是64位,则jdk也需要为64位

image.png



六、其他

注意:开发环境可以安装完整的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



http://www.lryc.cn/news/408658.html

相关文章:

  • C# datetimePicker
  • AI有关的学习和python
  • 前端node.js入门
  • 无需标注的数据集
  • C# 抽象工厂模式
  • java中 两个不同类对象list,属性一样,如何copy
  • 文件上传总结
  • 网页突然被恶意跳转或无法打开?DNS污染怎么解决?
  • Matlab进阶绘图第65期—带分组折线段的柱状图
  • EasyMedia转码rtsp视频流flv格式,hls格式,H5页面播放flv流视频
  • FPGA实验6: 有时钟使能两位十进制计数器的设计
  • C# 委托函数 delegate
  • Vue3响应式高阶用法之`shallowReadonly()`
  • Windows系统安全加固方案:快速上手系统加固指南 (下)
  • 记一次因敏感信息泄露而导致的越权+存储型XSS
  • Java笔试面试题AI答之线程Thread(1)
  • 2.5 C#视觉程序开发实例2----图片内存管理
  • Java核心 - 深入理解 Java 枚举类
  • HOW - CSS 定义颜色值
  • Vue3 reactive原理(一)-代理对象及数组
  • 基于联咏 NT98692芯片赋能边缘计算IP摄像机与XVR监控系统解决方案
  • Python设计模式 - 工厂方法模式
  • 学习记录:ESP32控制舵机 FREERTOS BLE
  • react中的useState和Hook、副作用
  • Linux嵌入式学习——数据结构——线性表的链式结构
  • 文本编辑 文本中的各种空格
  • Vue插槽 (Slots)详解
  • Unity中有关Animation的一点笔记
  • module federation模块联邦与微前端
  • 日常开发记录分享——C#控件ToolTip实现分栏显示内容