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

C/C++中调用Java实现

为什么需要在C/C++中调用Java?

  1. 遗留系统或库

    • 某些遗留系统或库可能用 Java 编写,而你的 C/C++ 应用程序需要与这些系统或库集成。

  2. 跨平台兼容性

    • Java 以其“一次编写,到处运行”(Write Once, Run Anywhere)的能力而闻名。如果你需要一个跨多个操作系统的解决方案,而你的团队更熟悉 Java,可能会选择用 Java 编写核心逻辑。

  3. 生态系统和社区支持

    • Java 拥有庞大的生态系统和社区支持。如果项目依赖于特定的 Java 库或框架,可能需要在 C/C+ 应用程序中调用 Java 代码。

  4. 高级特性

    • Java 提供了许多高级语言特性,如自动内存管理和垃圾回收、丰富的标准库等,这些特性可能在 C/C+ 中难以实现或需要大量额外的工作。

  5. 并发处理

    • Java 拥有强大的并发处理能力,包括内置的多线程支持和并发工具。如果 C/C+ 应用程序需要执行复杂的并发任务,可能会考虑使用 Java。

  6. 集成第三方服务

    • 许多第三方服务和 API 提供 Java 绑定或客户端,可能没有 C/C++ 绑定。在这种情况下,你可能需要从 C/C+ 应用程序中调用 Java 代码来与这些服务交互。

  7. 性能优化

    • 在某些情况下,你可能需要在 C/C+ 中实现性能关键型代码,同时利用 Java 提供的其他功能。通过在 C/C+ 中调用 Java,可以在一个应用程序中混合使用两种语言的优势。

  8. 团队专长

    • 项目团队可能在 Java 方面有特定的专业知识或资源,而 C/C+ 团队可能在系统的核心功能或性能优化方面更为擅长。

注意事项

虽然在 C/C+ 中调用 Java 是可能的,但需要注意以下事项:

  • JNI 的复杂性:Java Native Interface (JNI) 可以复杂且难以正确使用。它增加了代码的复杂性,并可能导致不稳定和难以维护的应用程序。

  • 性能开销:JNI 调用可能引入额外的性能开销,特别是在需要频繁调用的情况下。

  • 内存管理:JNI 需要手动管理内存,这可能导致内存泄漏或其他内存相关的问题。

在决定在 C/C+ 应用程序中调用 Java 之前,应该仔细评估是否真的需要这么做,以及这么做的潜在影响。在某些情况下,可能更合理的选择是将 Java 代码重写为 C/C+,或者寻找可以替代 Java 功能的 C/C+ 库。

必备环境

1.必须要在电脑上安装Java环境。配置环境参考文章:

Java详细安装配置教程(Windows),从下载到配置——Java-1.8(jdk)安装_jre1.8-CSDN博客

在C++代码中需要配置JVM的动态库才可以使用。

实现步骤

在C++中调用Java代码通常通过Java Native Interface(JNI)来实现。以下是详细的步骤和示例代码:

步骤 1:编写Java代码

首先,编写一个Java类,其中包含你希望从C++调用的方法。例如:

public class Sample2 {public String name;public static String sayHello(String name) {return "Hello, " + name + "!";}public String sayHello() {return "Hello, " + name + "!";}
}

步骤 2:编译Java代码并生成JNI头文件

使用javac编译Java代码,并使用javah工具生成JNI头文件。javah工具在较新的JDK版本中已被javac -h替代。

javac Sample2.java

这将生成一个名为JavaClass.class 的JNI头文件。

步骤 3:编写C++代码

在C++代码中,使用JNI调用Java方法。以下是示例代码:

// #include <iostream>// using namespace std;// int main()
// {
//     cout << "cmake Hello World!" << endl;//     return 0;
// }#include <iostream>
using namespace std;
#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=.";// options[0].optionString = "-Djava.class.path=E:\\C++\\src\\C++_call_java\\java_src";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);cout<< status<<endl;if (status != JNI_ERR){cout<<"open JVM"<<endl;}else{cout<<"error open JVM"<<endl;return -1;}cls = env->FindClass("Sample2");if(cls == 0){cout<<"NO class "<<endl;return -2;}cout<<"获取到了class对象"<<endl;
#if 0cout<<"========================1==========================="<<endl;// 获取方法ID, 通过方法名和签名, 调用静态方法mid = env->GetStaticMethodID(cls,"sayHello","(Ljava/lang/String;)Ljava/lang/String;");if (mid == 0){cout<<"NO method "<<endl;return -2;}const char* name = "World";//从C转换为java的字符, 使用NewStringUTF方法jstring arg = env->NewStringUTF(name);//调用静态方法jstring result =(jstring)env->CallStaticObjectMethod(cls,mid,arg);// 从java转换为C的字符, 使用GetStringUTFCharsconst char*str = env->GetStringUTFChars(result,0);printf("Result of sayHello: %s\n", str);env->ReleaseStringUTFChars(result,0);
#elsecout<<"========================2==========================="<<endl;/*** 新建一个对象 ***/// 调用默认构造函数//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");return -2;}// 获取属性ID, 通过属性名和签名fid = env->GetFieldID(cls,"name","Ljava/lang/String;");if(fid ==0){printf("attribute get failed.\n");}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){cout<<"NO method "<<endl;return -2;}// 调用成员方法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();
#endif#if 0// 启动虚拟机status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);if (status != JNI_ERR){// 先获得class对象cls = (*env)->FindClass(env, "Sample2");if (cls != 0){// 获取方法ID, 通过方法名和签名, 调用静态方法mid = (*env)->GetStaticMethodID(env, cls, "sayHello", "(Ljava/lang/String;)Ljava/lang/String;");if (mid != 0){const char* name = "World";jstring arg = (*env)->NewStringUTF(env, name);jstring result = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid, arg);const char* str = (*env)->GetStringUTFChars(env, result, 0);printf("Result of sayHello: %s\n", str);(*env)->ReleaseStringUTFChars(env, result, 0);}/*** 新建一个对象 ***/// 调用默认构造函数//obj = (*env)->AllocObject(env, cls);// 调用指定的构造函数, 构造函数的名字叫做<init>mid = (*env)->GetMethodID(env, cls, "<init>", "()V");obj = (*env)->NewObject(env, cls, mid);if (obj == 0){printf("Create object failed!\n");}/*** 新建一个对象 ***/// 获取属性ID, 通过属性名和签名fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");if (fid != 0){const char* name = "icejoywoo";jstring arg = (*env)->NewStringUTF(env, name);(*env)->SetObjectField(env, obj, fid, arg); // 修改属性}// 调用成员方法mid = (*env)->GetMethodID(env, cls, "sayHello", "()Ljava/lang/String;");if (mid != 0){jstring result = (jstring)(*env)->CallObjectMethod(env, obj, mid);const char* str = (*env)->GetStringUTFChars(env, result, 0);printf("Result of sayHello: %s\n", str);(*env)->ReleaseStringUTFChars(env, result, 0);}}(*jvm)->DestroyJavaVM(jvm);return 0;}else{printf("JVM Created failed!\n");return -1;}
#endif}

步骤 4:加载Java虚拟机(JVM)

在C++代码中,需要加载JVM并获取JNIEnv指针。以下是加载JVM的代码:

JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[1];options[0].optionString = "-Djava.class.path=.";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;jint res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res < 0) {std::cerr << "Failed to create JVM" << std::endl;return -1;
}

步骤 5:调用Java方法

在C++代码中,使用JNIEnv指针调用Java方法。这已经在前面的callJavaMethod函数中展示。

步骤 6:清理资源

在程序结束时,需要清理资源并关闭JVM:

jvm->DestroyJavaVM();

注意事项

  • 异常处理:在调用Java方法时,可能会抛出异常。使用env->ExceptionOccurred()env->ExceptionClear()来处理异常。

  • 数据类型转换:JNI提供了函数来处理Java和C++之间的数据类型转换。

  • 性能开销:频繁的JNI调用可能会带来性能开销,因此建议在非频繁交互的场景中使用。

通过以上步骤,你可以在C++程序中调用Java代码,利用JNI在两种语言之间传递数据和功能。

文件编译

pro文件

TEMPLATE = app
CONFIG += console c++17
CONFIG -= app_bundle
CONFIG -= qtSOURCES += \main.cppINCLUDEPATH += E:/software/java/jdk/include E:/software/java/jdk/include/win32# LIBS+= -LE:\software\java\jdk\lib -ljvmLIBS +=-LE:\software\java\jdk\jre\bin\server -ljvm# QMAKE_CXXFLAGS += -fPIC

CMakeLists.txt文件 

cmake_minimum_required(VERSION 3.16)project(simple LANGUAGES CXX)set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)add_executable(simple main.cpp)
# include_directories target_include_directories  添加头文件的路径include_directories("E:/software/java/jdk/include" E:/software/java/jdk/include/win32 ) #ok
# include_directories("E:\software\java\jdk\include") #no
# include_directories("E:\\software\\java\\jdk\\include\\win32") #ok# set (JAVA_PATH E:/software/java/jdk/include)
# include_directories(${JAVA_PATH}/win32)# 设置 Java 安装路径
set(JAVA_HOME "E:/software/java/jdk")# 查找所有 DLL 文件
file(GLOB JavaDlls "${JAVA_HOME}/jre/bin/server/*.dll")# 链接 Java DLL 文件
target_link_libraries(simple ${JavaDlls})# link_directories(simple "E:\\software\\java\\jdk\\jre\\bin\\server\\")
# link_libraries(simple "E:\\software\\java\\jdk\\jre\\bin\\server\\jvm.dll")# 链接生成的库
# target_link_libraries(simple "E:\\software\\java\\jdk\\jre\\bin\\server\\jvm.dll")include(GNUInstallDirs)
install(TARGETS simpleLIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

二 Java调用C/C++


Java调用C/C++时,遵循几个步骤:
1、 用Java native关键字声明方法为本地方法(非Java语言实现)。
2、 编译该声明类,得到XXX.class文件。
3、 用“javah –jni XXX”命令从该class文件生成C语言头文件(XXX.h)。
4、 采用C语言实现该头文件声明的方法,将实现类编译成库文件(libXXX.so)。
5、在Java程序中使用System.loadLibrary(XXX)加载该库文件(需要设置-Djava.library.path环境变量指向该库文件存放路径)。
6、 即可象调用Java方法一样,调用native方式声明的本地方法。

(暂时未用到,后续如有需要联系作者进行补充)

http://www.lqws.cn/news/521695.html

相关文章:

  • keil5 cannot copy license file to “Download“ folder
  • 阿里云Web应用防火墙3.0使用CNAME接入传统负载均衡CLB
  • 量学云讲堂王岩江宇龙2025年第58期视频 主课正课系统课+收评
  • 【EDA软件】【应用功能子模块网表提供和加载编译方法】
  • Web层注解
  • 浙大/浙工大合作iMeta(1区 | IF 33.2):单微生物RNA-seq + 聚类解析肠道关键种代谢功能
  • MySQL常用函数性能优化及索引影响分析
  • ES和 Kafka 集群搭建过程中的典型问题、配置规范及最佳实践
  • C++11原子操作:从入门到精通
  • Fisco Bcos学习 - 搭建第一个区块链网络
  • selenium UI自动化元素定位中classname和CSS区别
  • Spring Boot中日志管理与异常处理
  • 【评估指标】MAP@k (目标检测)
  • docker start mysql失败,解决方案
  • 深入理解Redis整数集合(intset)的升级策略:内存优化的核心魔法
  • FPGA笔记——ZYNQ-7020运行PS端的USB 2.0端口作为硬盘
  • 基于大数据的社会治理与决策支持方案PPT(66页)
  • IE浏览器使用
  • 系统思考:预防重于治疗
  • 如何搭建CDN服务器?
  • 将 Docker的存储目录迁移到空间更大的磁盘
  • 搭建自己的WEB应用防火墙
  • mbedtls ssl handshake error,res:-0x2700
  • 数据库数据恢复—SQL Server数据库被加密如何恢复?
  • Fisco Bcos学习 - 搭建星形拓扑组网
  • python基础
  • Android14音频子系统-Linux音频子系统ASoC-ALSA
  • Linux RDMA网络配置手册
  • 2026-软件工程-《软件质量测试与保证》-期末复习—习题汇总
  • 【编程基本功】Win11中Git安装配置全攻略,包含Git以及图形化工具TortoiseGit