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();   // 后面的代码只要退出应该都需要先释放VM
            return -1;
        }


        // 一、调用成员方法 getSomething 的示例
        {
            // 1. 获取成员方法getSomething的ID
            jmethodID getSomethingMethodid = env->GetMethodID(cls, "getSomething", "(Ljava/lang/String;)Ljava/lang/String;");
            if (getSomethingMethodid == nullptr) {
                std::cout << "getSomethingMethodid获取失败" << std::endl;
                return -1;
            }

            // 2. 调用成员方法getSomething
            jstring 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的ID
            jmethodID addNumberMethodid = env->GetMethodID(cls, "addNumber", "(II)I");
            if (addNumberMethodid == nullptr) {
                std::cout << "addNumberMethodid获取失败" << std::endl;
                return -1;
            }

            // 2. 调用成员方法getSomething
            jint 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);
    }


    // 释放jvm
    if (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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值