java native函数库_Java之native函数

文章目录

1、native方法介绍

2、Java程序的编写和头文件生成

3、C++函数编写及dll生成

3.1、创建C++工程

3.2、修改编译器设置

3.3、添加头文件

3.4、修改Main.h以及实现其中的函数

3.5、编译生成dll文件并运行Java程序

4、题外话

我们在使用hashmap时,有时候会看到hashcode的源码。最原始的hashcode源码是位于Object类中,是一个native方法。native方法是指,它没有方法体,不是用Java语言实现的。而是通过动态链接库(dll)加载而调用的函数。动态链接库一般使用C、C++、汇编语言等编写,这就实现了和底层函数的交互,类似于API的功能。我们今天就来实现一个native方法。当然,操作系统是windows10,64位。Linux也类似,只不过生成的动态链接库是so文件。

1、native方法介绍

Java虽然功能强大,但是存在一个缺点,就是JVM本身也是一个动态链接库(内部有class文件的解释器),它加载类和解释执行的效率不如直接编译的C++高。再有就是,Java设计系统API等底层操作时可能无能为力。一些经常调用的函数,或者和操作系统交互的函数必须用其他语言来完成。

Java中的JNI(Java Native Interface)就是实现native方法的途径。它通过C/C++的编程接口(头文件)来达到和C/C++交互的目的。

我们先来看一下,native方法的执行过程。

首先,在类被加载时,需要加载native方法实现的动态链接库,因此这段加载代码必须是静态加载器中的程序段。比如下面的ExampleClass类:

public class ExampleClass {

static {

System.load("example.dll");

}

public native void example();

}

然后,当JVM执行到native函数时,查找已经加载好的动态链接库,如果找到对应函数的实现,则把执行权转交操作系统,操作系统将进程调度至动态链接库,开始函数执行,Java程序则等待其返回值;如果未找到则报错(属于Error型异常,也就是JVM级别的异常,不可捕获)。

了解到这一点,我们就可以编写自己的native方法了。程序员出生时说的第一句话(滑稽)是Hello World,所以我们也以Hello World为例。环境如下:

操作系统:windows 10 64bit

Java IDE:IDEA 2018.3–64bit(eclipse的小伙伴自行对应)

JDK:1.8(AMD64)

C++ IDE:codeblocks 17

C++ compiler:mingw64-g++-8.1(注意,codeblocks自带的编译器是32位的,生成的动态链接库也是32位的,不能被64位的JVM加载。所以稍后我们将手动修改codeblocks的编译器路径)

2、Java程序的编写和头文件生成

我们首先新建Java工程,编写一个简单的Java程序,直接在主类中就行。如下:

public class Main {

static {

System.load("D:\\jni.dll");

}

public native static void hello(); // 必须有static,因为静态函数不能直接调用同一个类的非静态函数

public static void main(String[] args) {

hello();

}

}

这个hello函数就是我们要实现的native函数,功能是输出字符串“Hello World”。在这个主类中,需要加载D:\jni.dll,所以把它写在static初始化器中。

接下来,我们需要编译出class文件,并生成一个C++的头(.h)文件。我们单击idea的一键编译运行即可。这次运行是一定会报错的,提示无法加载动态链接库。但我们不需要运行,只需要那个class文件。

找到你的class文件所在位置。比如,我的是在工程目录\out\production\doorsymbol下,其中doorsymbol是我的工程名。eclipse的小伙伴自行找。反正看到Main.class就对了。如下图:

30c0c7b2f3fdd01a59225d25c7b24122.png

然后,打开命令行,转到当前目录下。输入命令:

javah -jni Main

之后便会看到一个Main.h文件。这一步算大功告成。

3、C++函数编写及dll生成

3.1、创建C++工程

这一步我们将生成dll文件。我们用codeblocks新建一个C++工程。注意,这次不是控制台程序,而是动态链接库了。工程目录不要有空格,比如我选择D:\cppprojects\jni

这一步一定要选这个:

93b5bdd7664d3c3aeab051a36f90955a.png

3.2、修改编译器设置

刚才提到,codeblocks自带的编译器是mingw32,无法编译64位的动态链接库。所以我们需要把默认编译器路径改成mingw64所在的路径。没有mingw64的小伙伴可以下载这里的压缩包并解压。链接:https://pan.baidu.com/s/1jsxoCnntqCo4xRBIFF1ACw

提取码:tian

解压后,转到mingw64文件夹中。如图:

a0bc20d1183692ad3dc629c69897e8f9.png我们只需要把编译器路径改成这个文件夹路径即可。

codeblocks–settings–compiler

c9c760933c35c8617b2dab84c0195f26.png点可执行工具链(executable tool chain)选项卡,把编译器的安装目录改成刚才的mingw64文件夹。

接着,修改编译选项。单击compiler settings选项卡。选择遵循c++11规范,以及生成x86_64目标。尤其是生成64位目标至关重要,这将直接决定我们生成动态链接库的格式。

1b309a1c83713920ae808001db86d314.png

单击确定。

然后再修改调试器路径,也就是gdb。如下图:

settings–debugger settings

b46ea9919d8693adfe63ecb6c2a378c0.png单击default选项卡,把调试器路径(executable path)改成刚才的mingw64文件夹下的bin/gdb.exe即可。单击确定。

3.3、添加头文件

编译器设置好之后,我们的工程中,有一个默认的main.cpp文件和main.h文件。其中main.h文件没有任何用处,把它删掉。我们需要用的是刚才用class文件生成的Main.h。打开工程目录,把这个头文件移动到和main.cpp同级目录下。然后,打开你的jdk路径(以下简称java_home),如下图:

f6c45aa9759033494cc5c806df5ffc1d.png把以下两个头文件放入和main.cpp同级的目录下:

%java_home%\include\jni.h

%java_home%\include\win32\jni_md.h

最终的效果如下图

ce610b0146e1c889c8f07fe3ba9e4954.png

3.4、修改Main.h以及实现其中的函数

打开Main.h,发现它声明了一个函数Java_Main_hello。这个函数就是Java中native函数hello的真正接口。另外,我们看到头文件包含了#include,我们把它改成#include "jni.h"。

然后打开main.cpp文件,删除全部内容,改成如下:

#include "Main.h"

#include

using namespace std;

JNIEXPORT

void JNICALL Java_Main_hello(JNIEnv *, jclass)

{

cout << "Hello World!" << endl;

}

注意一定要包含头文件Main.h,并且函数头必须与Main.h中的声明完全相同。为避免出错,建议复制粘贴。

3.5、编译生成dll文件并运行Java程序

这一切都完毕之后,单击编译按钮,如果没有出错,则生成动态链接库成功。我们到工程目录下bin\Debug中,找到一个dll文件。把它复制到D盘根目录下,并改名为jni.dll。

668ffe8e4990a8be9c3ed0055c918418.png

返回IDEA,再次运行Java程序,可见输出Hello World!字样。这样,我们的native函数就圆满成功了。

4、题外话

思考:如果一个类有多个native函数是什么情况?如果有多个含native函数的类,又是什么情况?

答案:一个类多个native,只生成一个头文件,但头文件中有多个函数声明。多个含native的类,则每个类分别生成一个头文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值