C/C++如何调用Java

article/2025/10/17 0:23:51

前言

简单介绍C/C++如何调用Java,内容适合未接触过此类工程的朋友作为上手参考。


一、编译环境

1. 64位的win10系统

2. JDK:jdk-8u181-windows-x64.exe

3. IDE VS2017

二、调用步骤及Java虚拟机使用方法

一般步骤:

  1. 编写Java代码, 并编译
  2. 编写C/C++代码
  3. 配置lib进行编译,配置PATH添加相应的dll

使用方法:

  1. 创建虚拟机
  2. 寻找class对象, 创建对象
  3. 调用静态方法和成员方法
  4. 获取成员属性, 修改成员属性

三、编译过程

1、配置环境变量

在这里插入图片描述
添加环境变量JAVA_HOME,路径为Java安装路径
在这里插入图片描述
在path里面添加路径,路径为bin目录所在路径和jvm.dll所在路径

2、构建被调用Java类

一段简单的代码
代码如下(示例):

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

运行下面的命令编译

javac Sample2.java

可以在当前目录下看到Sample2.class文件, 编译成功.。

可以查看Sample2类中的签名

javap -s -private Sample2

结果如下

Compiled from "Sample2.java"
public class Sample2 {public java.lang.String name;descriptor: Ljava/lang/String;public Sample2();descriptor: ()Vpublic static java.lang.String sayHello(java.lang.String);descriptor: (Ljava/lang/String;)Ljava/lang/String;public java.lang.String sayHello();descriptor: ()Ljava/lang/String;
}

3、创建C/C++调用Java工程(VS控制台工程x64)

c++示例代码如下:

#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=D:/c_call_java/javaclass/";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);if (status != JNI_ERR){// 先获得class对象cls = env->FindClass("Sample2");if (cls != 0){// 获取方法ID, 通过方法名和签名, 调用静态方法mid = env->GetStaticMethodID( cls, "sayHello", "(Ljava/lang/String;)Ljava/lang/String;");if (mid != 0){const char* name = "World";jstring arg = env->NewStringUTF(name);jstring result = (jstring)env->CallStaticObjectMethod(cls, mid, arg);const char* str = env->GetStringUTFChars(result, 0);printf("Result of sayHello: %s\n", str);env->ReleaseStringUTFChars(result, 0);}/*** 新建一个对象 ***/// 调用默认构造函数//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");}/*** 新建一个对象 ***/// 获取属性ID, 通过属性名和签名fid = (env)->GetFieldID( cls, "name", "Ljava/lang/String;");if (fid != 0){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){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();return 0;}else{printf("JVM Created failed!\n");return -1;}
}

这段代码大概做了这几件事

1.创建虚拟机JVM, 在程序结束的时候销毁虚拟机JVM
2.寻找class对象
3.创建class对象的实例
4.调用方法和修改属性

工程相关配置如下所示:
添加包含目录
![(https://img-blog.csdnimg.cn/3769c5cbaafc45f989651a2b4451cff7.png)
添加库目录
在这里插入图片描述
引用库文件jvm.lib
在这里插入图片描述
运行结果:

Result of sayHello: Hello, World!
Result of sayHello: Hello, icejoywoo!

4、虚拟的创建

与之相关的有这样几个变量

JavaVMOption options[1];     
JNIEnv *env;     
JavaVM *jvm;     
JavaVMInitArgs vm_args;

JavaVM就是我们需要创建的虚拟机实例

JavaVMOption相当于在命令行里传入的参数

JNIEnv在Java调用C/C++中每个方法都会有的一个参数, 拥有一个JNI的环境

JavaVMInitArgs就是虚拟机创建的初始化参数, 这个参数里面会包含JavaVMOption

下面就是创建虚拟机

options[0].optionString = "-Djava.class.path=.";
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);

"-Djava.class.path=."看着眼熟吧,这个就是传入当前路径,作为JVM寻找class的用户自定义路径(当然也可以不在当前路径,你可以随便修改)。

vm_args.version是Java的版本, 这个应该是为了兼容以前的JDK, 可以使用旧版的JDK, 这个宏定义是在jni.h中, 有以下四种

#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
#define JNI_VERSION_1_4 0x00010004
#define JNI_VERSION_1_6 0x00010006

vm_args.nOptions的含义是, 你传入的options有多长, 我们这里就一个, 所以是1.

vm_args.options = options把JavaVMOption传给JavaVMInitArgs里面去.

然后就是启动虚拟机了status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args).

可以通过这个返回值status , 知道虚拟机是否启动成功

#define JNI_OK           0                 /* success */
#define JNI_ERR          (-1)              /* unknown error */
#define JNI_EDETACHED    (-2)              /* thread detached from the VM */
#define JNI_EVERSION     (-3)              /* JNI version error */
#define JNI_ENOMEM       (-4)              /* not enough memory */
#define JNI_EEXIST       (-5)              /* VM already created */
#define JNI_EINVAL       (-6)              /* invalid arguments */

寻找class对象, 并实例化
JVM在Java中都是自己启动的,在C/C++中只能自己来启动了, 启动完之后的事情就和在Java中一样了, 不过要使用C/C++的语法.

获取class对象比较简单,FindClass(env, className).

cls = env->FindClass("Sample2");

在Java中的类名格式是java.lang.String, 但是className的格式有点不同, 不是使用’.‘作为分割, 而是’/', 即java/lang/String.

我们知道Java中构造函数有两种, 一种是默认的没有参数的, 一种是自定义的带有参数的. 对应的在C/C++中, 有两种调用构造函数的方法.

调用默认构造函数

// 调用默认构造函数

obj = env->AllocObject(cls);

构造函数也是方法, 类似调用方法的方式.

// 调用指定的构造函数, 构造函数的名字叫做

mid = env->GetMethodID(cls, "<init>", "()V");
obj = env->NewObject(cls, mid);

调用方法和修改属性

关于方法和属性是有两个ID与之对应, 这两个ID用来标识方法和属性.

jmethodID mid;
jfieldID fid;

方法分为静态和非静态的, 所以对应的有

mid = env->GetStaticMethodID(cls, "sayHello", "(Ljava/lang/String;)Ljava/lang/String;");
mid = env->GetMethodID(cls, "sayHello", "()Ljava/lang/String;");

上面两个方法是同名的, 都叫sayHello, 但是签名不同, 所以可以区分两个方法.

JNI的函数都是有一定规律的, Static就表示是静态, 没有表示非静态.

方法的调用如下

jstring result = (jstring)env->CallStaticObjectMethod(cls, mid, arg);
jstring result = (jstring)env->CallObjectMethod(obj, mid);

我们可以看到静态方法是只需要class对象, 不需要实例的, 而非静态方法需要使用我们之前实例化的对象.

属性也有静态和非静态, 示例中只有非静态的.

获取属性ID

fid = env->GetFieldID(cls, "name", "Ljava/lang/String;");

修改属性的值

env->SetObjectField(obj, fid, arg); // 修改属性

关于jstring的说明

java的String都是使用了unicode, 是双字节的字符, 而C/C++中使用的单字节的字符.

从C转换为java的字符, 使用NewStringUTF方法

jstring arg = env->NewStringUTF(name);

从java转换为C的字符, 使用GetStringUTFChars

const char* str = env->GetStringUTFChars(result, 0);

四、总结

编译和运行

  1. 编译需要头文件, 头文件在这两个目录中%JAVA_HOME%\include和%JAVA_HOME%\include\win32, 第一个是与平台无关的, 第二个是与平台有关的, 由于笔者的系统是windows, 所以是win32.
  2. 编译的时候还要一个lib文件(jvm.lib), 是对虚拟机的支持, 保证编译通过.
  3. 我们可以看到在当前目录下Sample2.exe, 运行的时候需要jvm.dll(不要将其复制到当前目录下, 这样不可以运行, 会导致jvm创建失败)
  4. jvm.dll在%JAVA_HOME%\jre\bin\server\目录下, 所以我把这个目录加入到PATH中, 然后就可以运行

五、注意

  1. 动态链接库和JDK都有32位和64位的区别, 使用32位系统的朋友, 要注意这个问题, 可能导致运行或编译错误.(笔者这里使用的是64位)
  2. 还要注意区分C和C++代码, 在JNI中两种代码有一定的区别, 主要是env和jvm两个地方.

http://chatgpt.dhexx.cn/article/TxePuS6g.shtml

相关文章

java调用C

文章目录 idea创建一个java类通过命令生成.h文件vs2015创建dll项目修改dll项目的属性补充dll项目的头文件和源文件导入文件源文件的制作 生成dll文件idea导入dll文件idea使用函数 这个确实卡了我一段时间。 先说说必要性吧。 java无法处理的操作&#xff08;指向地址&#xff…

Java调用c/c++(JNI)最详细步骤

一、JNI(Java Native Interface)的作用就是Java通过JNI调用其他语言的函数(或方法)&#xff08;主要是C&C&#xff09;。 二、准备 1. java8系列jdk&#xff0c;有很多版本&#xff0c;任选一个即可&#xff0c;如jdk1.8.0.231。安装好&#xff0c;配置好环境。 2. vs20…

使用java调用C语言程序教程

1.idea创建一个java类 严格来说&#xff0c;核心步骤并不是创建一个java类&#xff0c;而是创建一个方法&#xff0c;那个方法要被native修饰&#xff0c;这才是关键。 接着在TestNativeCode类当中声明我们的本地方法&#xff1a; package com.wwj.nativecode; public class T…

linux中文语言包下载地址,centos中文语言包-官方版-centos中文语言包fonts-chinese-3.02-12.el5.noarch.rpm-独木成林...

centos中文语言包 fonts-chinese-3.02-12.el5.noarch.rpm&#xff0c;直接在目录下运行&#xff1a; rpm -ivh fonts-chinese-3.02-12.el5.noarch.rpm linux中文文件名乱码的解决办法(安装中文支持包) 由于安装英文版的系统不支持中文&#xff0c;出现中文文件名乱码。 下面操作…

Linux-centos安装MySQL8.0.22连接驱动文件mysql-connector-java-8.0.22-1.el7.noarch.rpm

目录 1、下载地址 2、选择版本 3、安装驱动 1、下载地址 https://downloads.mysql.com/archives/c-j/ 2、选择版本 选择对应的版本&#xff0c;这里系统选择Redhat系列Linux7&#xff0c;驱动版本选择8.0.22&#xff0c;点击download下载到本地再上传至服务器安装。 也可…

Mysql-MHA 安装过程中遇到的问题 :报错rpm -ivh mha4mysql- manager- 0.56-0.el6.noarch.rpm错误:依赖检测失败:

rpm -ivh mha4mysql- manager- 0.56-0.el6.noarch.rpm 错误&#xff1a;依赖检测失败&#xff1a; perl(Config::Tiny) 被 mha4mysql-manager-0.56-0.el6.noarch 需要 perl(Log::Dispatch) 被 mha4mysql-manager-0.56-0.el6.noarch 需要 perl(Log::Dispatch::File) 被 mha4mysq…

Linux下载并安装rabbitmq-server-3.6.5-1.noarch.rpm

目录 1.安装rabbitmq所需要的依赖包 2.下载安装包 3.安装服务命令 4.修改配置 5.启动rabbitmq 6.rabbitmq控制台安装 7.访问你的虚拟机 ip:15627 会出现下面的页面 用户名和密码都是 guest 8.常用命令 1.安装rabbitmq所需要的依赖包 yum install build-essential o…

centos rpm安装mysql时依赖检测失败:mysql57-community-release 与 mysql80-community-release-el8-3.noarch 冲突

1.出现以上报错&#xff0c;说明MySQL安装版本冲突 首先卸载原来的MySQL rpm -qa |grep mysql //搜索安装的MySQL包rpm -e --nodeps 搜索出来的包名 //卸载示例&#xff1a; [rootcentos8s ~]# rpm -qa |grep mysql mysql57-community-release-el7-11.noarch [rootcent…

yum 安装daemonize 错误:依赖检测失败: daemonize 被 jenkins-2.303.1-1.1.noarch 需要

错误&#xff1a;依赖检测失败&#xff1a; daemonize 被 jenkins-2.303.1-1.1.noarch 需要 安装 epel-release yum -y install epel-release安装 daemonize yum -y install daemonize结果&#xff1a;

php代码解决乱码问题

在用php的passthru函数的时候&#xff0c;然后出现了乱码的情况 代码&#xff1a; <?php echo passthru("ipconfig"); ?> 解决方法&#xff1a; 在原来的代码前面加 header("content-type:text/html;charsetgbk2312"); charset——后面是修改…

PHP-MySQL乱码[?]问题处理

PHP-MySQL乱码[?]问题处理 PHP-MySQL乱码[?]问题处理问题展示原因分析查看数据库编码格式WindowsLINUX 解决方案1、修改MySQL配置文件2、修改php连接数据库的编码格式。 修改后结果展示 PHP-MySQL乱码[?]问题处理 问题展示 php执行sql语句返回结果为’???’ 原因分析…

PHP文件乱码解决

1.查看项目的编码格式是不是UTF-8 2.在页面的开始处加入下面代码 <?php header("content-type:text/html;charsetutf-8"); //设置编码 ?>3.如果是html和PHP语言混合的PHP文件&#xff0c;还需要<head>标签下&#xff0c;加如下代码&#xff1a; <…

php如何解决中文乱码问题?

首先 乱码为什么会发生&#xff1f; 乱码的发生大致有2种原因&#xff0c;一种是编码(charset) 设置错误&#xff0c;导致浏览器以错误的编码来解析&#xff1b;另一种是文件被以错误的编码打开&#xff0c;然后保存&#xff0c;比如一个文本文件原先是GB2312 编码的&#xff…

php页面上显示中文乱码怎么解决,php输出中文乱码问题怎么解决?,php页面中文显示乱码...

php输出中文乱码问题怎么解决&#xff1f;怎么解决PHP输出中汉字乱码问题&#xff1f;&#xff0c;解决方案&#xff1a;1。对于HTML和PHP混合的页面&#xff0c;应该使用元标签或者header()函数来统一页面编码&#xff1b;2.对于纯PHP的静态页面&#xff0c;使用header()函数将…

解决html、php中文乱码问题

我们将乱码情况分为以下几种&#xff0c;有需要的可以对照下面的几种情况有针对性的解决乱码问题 第一种&#xff1a;解决HTML中中文乱码问题方法 如果你的HTML文件文件出现了乱码问题&#xff0c;那么你可以在head标签里面加入UTF8编码(国际化编码)&#xff1a;UTF-8是没有国…

解决 PHP 输出中文乱码的问题

本文由荒原之梦原创&#xff0c;原文链接&#xff1a;http://zhaokaifeng.com/?p1340 解决 PHP 输出中文乱码的问题 问题描述 今天给导航狗(https://daohanggou.cn/)的 PHP 程序和数据库文件迁移了服务器, 但是迁移到新的服务器上之后 PHP 输出的中文和 PHP 输出的从 MySQL…

原创:PHP乱码怎么办?五种方法彻底解决PHP乱码问题

对于刚接触PHP的新手来说&#xff0c;乱码可能会让你边临崩溃&#xff0c;但是你要习惯&#xff0c;既然想学好PHP&#xff0c;乱码是家常便饭&#xff0c;不管是老鸟还是新手&#xff0c;都会遇到乱码的问题&#xff0c;下面是我总结的几个常见并且有效的解决方法&#xff1a;…

win7 装MacOS虚拟机做iOS开发

1. 下载安装Vmware Player 2. 下载MacOS10.6 Image http://www.verycd.com/topics/2897839/ [Mac.OS.X.操作系统].Mac.OS.X.Lion.10.7.VMware.Pre-Installed.Image.zip 详情 转载于:https://www.cnblogs.com/technical/archive/2012/04/13/2445417.html

求助。VMware上的MacOS虚拟机安装出现蓝色界面

请教&#xff01;联想小新air14IWL可以安装什么版本得MacOS镜像呢&#xff1f; 目前尝试了两个版本好像是10.15和10.12&#xff0c;截图放下面了 进去总是显示最后那张&#xff0c;根据教程虚拟机的文件目录里也都做了更改还是没有变化 请问是版本的问题还是什么有问题&…

自制macOS安装镜像iso虚拟机用

在网上下载的用于在虚拟机中安装的镜像版本相对比较旧。安装完成后还要进行升级比较麻烦。于是我就想自己制作安装镜像了。 精华 #创建空白磁盘镜像 hdiutil create -o /tmp/ventura -size 13800m -volname ventura -layout SPUD -fs HFSJ #挂载上面创建的镜像 hdiutil attac…