(Java Native Interface, JNI)java调用c/c++

本文通过一个简单实例介绍如何使用Java Native Interface (JNI)实现Java与C/C++代码的交互。包括创建本地方法、生成JNI头文件、编写及编译C/C++实现代码、加载本地库并调用本地方法等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java本身提供了jni(Java Native Interface) ,使得java可以与其他语言的代码(主要是c/c++)。

其实jni相对来说还是比较简单的,举一个简单的例子:

新建一个HelloNative.java

class HelloNative
{
	public static native void greeting();
}

然后在控制台下,并设置当前目录为工作目录(cd或者按住shift并点击右键打开命令窗口),输入命令javah HelloNative,会在当前目录下生成一个对应的头文件HelloNative.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloNative */

#ifndef _Included_HelloNative
#define _Included_HelloNative
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloNative
 * Method:    greeting
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloNative_greeting
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

这里可以把函数名括号前的几个空格去掉,免得在编译动态库时出错。

之后再编写对应的实现函数文件HelloNative.c

#include <stdio.h>

#include "HelloNative.h"

JNIEXPORT void JNICALL Java_HelloNative_greeting(JNIEnv *, jclass)
{
	printf("Hello Native World\n");
}

然后可以使用各种工具编译上述代码并生成动态库,比较简单的就是使用vs,让vs生成对应的动态库,先创建一个空项目,导入对应的文件,比如本例中,有HelloNative.h HelloNative.c jni.h jni_md.h。

注意这里的平台是64位,因为我的java 是64位的。32位和64位的区别,大家可以看看这个帖子:

https://blog.youkuaiyun.com/woainishifu/article/details/54017550

这里注意一下jni.h(从jdk下的include) 和jni_md.h(include/win32下)。

然后生成的dll文件放到对应的java文件夹中,之后就开始测试:

HelloNativeTest.java

class HelloNativeTest
{
	public static void main(String[] args)
	{
		HelloNative.greeting();
	}
	static
	{
		System.loadLibrary("HelloNative");
	}
}

代码不算多,也相对比较简单。在程序运行时,先执行static包含的静态代码块,加载HelloNative库,然后在主函数中调用对应的函数。 


 System.loadLibrary(String libname)

加载libname原生库(native library)。libname参数一定不能包含任何平台相关的前缀、扩展名或者是路径。如果libname链接VM成功,之后则会调用库导出的JNL_OnLoad_libname函数。另外,libname从系统库位置加载,并以依赖于实现的方式映射到本机库映像。


运行无误的话,控制台会有以下输出:

另外注意,java中的类型是与平台无关的,而c/c++的数据类型是平台相关的。为此,java为了保证一致性,在声明native方法时,需要使用jni文件中的类型。

 

javac/c++字节
booleanjboolean1
bytejbyte1
charjchar2
shortjshort2
intjint4
longjlong8
floatjfloat4
doublejdouble8

再在HelloNative类中新添加一个方法:

class HelloNative
{
	public static native void greeting();
	public static native int print(int width, int precision, double x);
}

然后javah生成对应的头文件 HelloNative.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloNative */

#ifndef _Included_HelloNative
#define _Included_HelloNative
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloNative
 * Method:    greeting
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloNative_greeting(JNIEnv *, jclass);

/*
 * Class:     HelloNative
 * Method:    print
 * Signature: (IID)I
 */
JNIEXPORT jint JNICALL Java_HelloNative_print(JNIEnv *, jclass, jint, jint, jdouble);

#ifdef __cplusplus
}
#endif
#endif

可以看到,方法后面的参数类型未jint,jint,jdouble。

然后进行对应的实现:

JNIEXPORT jint JNICALL Java_HelloNative_print(JNIEnv * env, jclass cl, jint width, jint precision, jdouble x)
{
	char fmt[30];
	int ret = JNI_FALSE;

	memset(fmt, '\0', sizeof(fmt));
	//"%width.precision f"
	sprintf(fmt, "%%%d.%df", width, precision);
	ret = printf(fmt, x);

	fflush(stdout);
	return ret;
}

这里先是给fmt数组进行初始化,然后输出。我在使用sprintf时,可能会出错

在vs中添加对应的宏即可:

之后编译即可。不过vs提供了对应的安全的sprintf_s函数,虽然我动态库编译成功,但是我在运行java函数会报错:

不知道sprintf_s内部使用了什么造成这个错误。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值