大家好,我是不会java的java废材生。某一天突然接到个小需求,要求用c++写一个函数并用java来调用,而且特地强调不允许通过网络请求的方式调用(当然作者比较废也不会写c++的服务接口),这就需要本地调用ddl动态链接库来实现了。作者以前没有做过类似是场景,不过java有性能瓶颈,所以一些偏复杂的功能可能就是用其他性能较好的语言来实现的(比如c、c++等)再在java里面调用那些实现好的功能,以下是作者做的案例并踩的一些坑,希望能帮助到大家。
我们在实现类似上面需求之前,要大致了解一下什么是ddl。
ddl是一种文件的类型,在Windows开启文件后缀名展示的话能看到它是应用程序扩展文件,标准点叫动态链接库文件(Dynamic Link Library)。在Linux上,动态链接库的文件后缀为.so(有兴趣的朋友可以自行搜索)。
dll文件是实现资源共享的一种方式,本质就是编译好的机器指令文件,直接打开就会看到一些乱码;动态是指只有在程序运行时才会去调用某个ddl,因此ddl性能开销就会小很多(静态链接的性能开销会大不少,具体有兴趣也可以百度)。
在生成dll之前,需要注意设置好编译环境,clion工具只提供了cmake项目构建工具。编译器的话一般选择用MinGW,MinGW要和jdk位数一样,64位的jdk对应64位的MinGW,MinGW下载链接。
设置->构建、执行、部署->工具链,按照下图配置好确定即可。
选择c++库,按下图勾选即可
其实选择可执行文件那个也是可以的,但我们简单打印个helloworld不需要执行测试。项目创建成功后会默认生成一个library.cpp文件和library.h文件。
1.代码编写
使用 __declspec(dllexport)标识符
新建一个cpp文件,直接在函数前面加上这个标识符就可以,该关键字可以将该函数标识为要导出的函数,如下面void hello()函数
注意事项:这种方式导出函数得到ddl之后,导出的函数名会改变,在编译链接的时候,c++会按照自己的规则篡改函数名称。
解决方式:在定义导出函数时加上extern"C"限定符
可以这样
也可以这样将多个要导出了函数放一个块里
完整代码附图如下
2.配置CMakeList.txt文件
在编写完上面代码之后就可以去生成dll文件了。
找到项目根目录下的CMakeList.txt配置文件(创建项目的话会默认生成),加入如下内容
因为创建的是c++库项目,所以配置里有add_library,如果是创建的是c++可执行文件的项目就要自己手动加入。
dlltest就是生成的dll文件名称,mytest.cpp就要导出为dll的代码源文件
配置好之后如下图
3.构建项目生成dll文件
点击最上方那栏构建->构建项目
项目构建成功之后会在cmake-build-debug目录下生成dll文件,不过默认加上了lib的前缀。
将这个dll文件复制到程序可以访问到的地方就可以调用那个void hello()函数了,如果访问不到会报错文件找不到。
注意:这一种方式少一些流程,但依然存在问题,只能解决c和c++之间调用函数命名的问题,生成了dll给java调用的时候就会报错java.lang.UnsatisfiedLinkError,想用java调用就用方式二吧。
了解java调用dll常见的三种方法
java调用dll常用有三种方法,分别是用jni、jna和jnative。
jni是jdk自带的,性能最好(推荐),其他两种是基于jni的封装。
本文使用jni的方法来调用。
了解java中native关键字
它用于声明一个方法是由本地代码实现的,也就是说这个方法的实现并不是用 java 编写的,而是使用其他语言(比如 c/c++)编写的,并通过jni调用。
如果希望在 java 中调用dll中的函数,并且不希望函数名被改变,你可以使用 native 关键字声明一个本地方法,并在本地代码中实现这个方法。
1.在java中创建一个类声明native方法
2.生成native方法对应的c++头文件
该头文件的作用是定义c++能够识别的java中方法对应的c++函数。
生成头文件需要用到命令,不同版本jdk所需命令不一样。
jdk1.8版本
javah命令,在MylibTest.java所在的目录下执行命令。
1.8版本以上
方式一
这个地方有坑,网上查的时候说是java8之后的版本用javac -h命令(但没补充,作者一执行就报错),这个命令是没问题但是如上面命令一样直接执行就会出问题。
我们能看到下面执行报错说无源文件。
那怎么解决能,指定一个输出源目录就可以了
-h 指定头文件的输出目录,.代表当前目录,test则是在当前目录下生成一个test目录
执行之后就会在MylibTest.java所在目录生成一个test目录
执行成功
生成test目录
test目录下会有一个头文件。
方式二
在与方式一同样的目录位置执行如下命令,encoding指定编码为uft8
作者jdk17这两种命令都用过了,实测没问题。
3.将生成的头文件引入c++项目
之前创建好了c++项目这里就不再创建了。
将生成的头文件复制粘贴到项目跟目录,生成的头文件里面用到了jni.h和jni_md.h头文件(jni.h和jni_md.h头文件在jdk目录下的include目录里面,找一找很快就能找到),也一并复制粘贴过来。
生成的头文件类容如下
4.在当前目录新建一个cpp文件并实现函数
新建的cpp需要引入生成的头文件和jni.h头文件,并实现生成头文件里面的函数
5.配置CMakeList.txt
在里面的add_library添加cpp和引用到生成的那个头文件(同前面的一样)
6.构建生成dll
原理同方式一提到的一样。
7.将生成的dll直接粘贴到jdk的bin目录下
bin目录是java在加载库时最优先访问的目录。
8.编写java测试类
9.运行测试
可以看到测试成功
在第一次弄的时候,踩了不少坑,弄了一个下午没弄出来(可能是作者比较菜的原因)。作者没有因此而放弃,查了诸多文章之后给实现出来了,总的来说多试试、多看看文章、多看看书,总会有收获的。
到此这篇java调用dll动态库代码(java如何调用dll库)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/jjc/70466.html