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.csdn.net/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");
}
}
代码不算多,程序运行时,先执行对应的静态代码块,加载进来对应的HelloNative库,然后在主函数中调用对应的函数。
运行无误的话,控制台会有以下输出:
另外注意,java中的类型是与平台无关的,而c/c++的数据类型是平台相关的。为此,java为了保证一致性,在声明native方法时,需要使用jni文件中的类型。
java | c/c++ | 字节 |
boolean | jboolean | 1 |
byte | jbyte | 1 |
char | jchar | 2 |
short | jshort | 2 |
int | jint | 4 |
long | jlong | 8 |
float | jfloat | 4 |
double | jdouble | 8 |
再在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内部使用了什么造成这个错误。。。