一、配置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(); // 后面的代码只要退出应该都需要先释放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;
}
代码运行结果:
其中:
- 调用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