众所周知,python调用dll动态链接库极其简单,加载一下dll文件,就可以调用其中的方法。那么目前仍热火朝天的Java能不能调用dll库,使用其中的方法呢?当然是可以的,但是相对于python来讲是稍微有一点困难。
那么Java是怎么调用dll动态链接库的呢?
大概分为以下几步:
1.创建Java类,在该类中加载dll文件,再用该类生成xxx.h文件
2.创建xxx.c文件,引用xxx.h头,实现xxx.h中的方法。
还有信心继续下去嘛?听着有点头大?又是h文件又是头文件的,糟心!别慌,准备一包瓜子咱们来调用dll中的一个实战一波你就明白了,下边我们实现一个Java调用dll中方法,实现两个数相加并返回控制台输出的简单案例。
1、先创建Java类,我这里创建了一个ConnectDll类(包名我也复制下来了)。
package com.csdn.dll;
public class ConnectDll {
static {
System.loadLibrary("");
}
public static native int add(int num1,int num2);
}
2.编译此类,生成对应的.class文件。
什么?不会生成?请自行百度javac命令。
3.将.class文件编译成 .h头文件。
很多人到这里就懵了,java文件还能转成.h文件么?是的,利用javah命令,可以将class字节码文件转换成.h文件。
众所周知,我们写项目,一般会在src下写很多级包名,然后在包里边写各个类,从上面的代码可以看出,我的包名为com.csdn.dll。但是我们生成项目文件的时候,系统会自动地把包名转换成各级文件夹。如果用javac命令将.java文件编译成class文件,需要进入到java文件所在的文件夹,但是用javah命令,只需要到包的上一级,即src目录下即可。然后用javah命令 +包名+类名。
具体命令: javah com.csdn.dll.ConnectDll (因为ConnectDll类的包名是com.csdn.dll)。
生成的.h文件就在src目录下。
4.让我们一窥.h文件的究竟。
学过C语言的都知道h是头文件,所以我认为h文件相当于Java的接口,C文件相当于java的实现类。(由于我C语言比较差,这里可能不太准确,如有大佬看到,请及时评论纠正。),下面是h文件中的东西,让我们一起看看。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_csdn_dll_ConnectDll */
#ifndef _Included_com_csdn_dll_ConnectDll
#define _Included_com_csdn_dll_ConnectDll
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_csdn_dll_ConnectDll
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_csdn_dll_ConnectDll_add
(JNIEnv *, jclass, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
从这个头文件中我们可以看到存在这样一个方法
JNIEXPORT jint JNICALL Java_com_csdn_dll_ConnectDll_add(JNIEnv *, jclass, jint, jint);
对比一下我们在ConnectDll类中写的方法
public static native int add(int num1,int num2);
我们可以发现一些东西还是比较相似的。如:jint ----->>int。
5.编写对应的实现文件C文件,生成dll库。
到了这里,我不知道大家是否还记得我们的初衷?我们的初衷是想用java调用dll文件,但是我们写了半天了也没见dll文件的生成以及调用啊?(之前接触过C的大佬可能知道,但是对于C语言本身就不怎么样的我来说,生成dll难为了我半天。)
以下内容C语言大佬可以自动忽略
如何生成dll文件?
我这里的工具是DevC++。(觉得Dev麻烦的可以忽略,然后自行百度其他软件生成dll)
1.打开Dev,文件->新建->项目,选择DLL项目,C语言,项目名称。
2.创建完项目,如下
3.将h和C文件书写完整保存,编译一下,就能在该项目目录中找到dll文件。
根据java生成的h文件,生成dll库并调用其中方法。
我们知道了如何生成dll文件,但是这跟我们java生成的.h文件又有什么关系呢?通过上面我们可以看到dll动态链接库实际上就是.h和.c编译链接生成的,我们通过dll文件可以调用dll中实现的方法。还是很混乱对吧,别急,我们先做,做完再回过头来看。
我们通过javah生成了.h文件,将生成的.h文件复制到这个项目中,并写对应的.c文件。H文件内容上边已给,C文件如下,
注意我这里引用的h文件是dll.h,我是把生成的h文件的内容复制到了dll.h下,文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_csdn_dll_ConnectDll */
#ifndef _Included_com_csdn_dll_ConnectDll
#define _Included_com_csdn_dll_ConnectDll
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_csdn_dll_ConnectDll
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_csdn_dll_ConnectDll_add
(JNIEnv *, jclass, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
这是C文件,文件内容如下
/* Replace "dll.h" with the name of your header */
#include "dll.h"
#include <windows.h>
JNIEXPORT jint JNICALL Java_com_csdn_dll_ConnectDll_add(JNIEnv * env, jclass cls, jint i, jint j){
return i+j;
}
点击编译,一般是会报错的,原因是没有 <jni.h>头文件。自行百度解决。
编译完成后,发现dll项目文件中多了一个后缀为.dll文件。这个就是我们要的。
6.Java文件加载Dll文件
在我们最开始的ConnectDll类中,有这么一段代码,System.loadLibrary("");这就是加载dll文件的代码,修改成这样System.loadLibrary("ConnectDll");(因为我的dll文件是ConnectDll.dll。)
7.编写Java测试类调用此方法
package com.csdn.test;
import com.csdn.dll.ConnectDll;
public class Test {
public static void main(String[] args) {
System.out.println(ConnectDll.add(3,5));
}
}
运行,会出现一堆错误,是因为你系统找不到你说的这个dll文件,怎么办呢?把这个dll文件放入JDK/bin目录中可以解决。
但是我们不能只知其然不知其所以然,System.loadLibrary("");这行代码的意思就是加载文件,但是它加载的是环境变量下的文件,系统会查找环境变量下有没有这个文件。当然加载文件还有一个方法System.load("");这个方法是加载绝对路径的文件。
8.综上所述
1.创建Java类
2.编译生成h头文件
3.实现头文件中的方法并生成dll
4.设置Java类中文件加载地址
9.展望
我们是Java开发者,我们不太可能会自己直接编写dll文件,一般都是调用别人写好的dll文件。我们可以自己编写一个dll文件来调用别人写好的dll,但是无论怎样都太过繁琐。我们程序员应该有一种精神叫"懒",我们接受不了这种太过复杂的调用,这样开发太过于繁琐。所以我们应该想着有没有什么简单的方式,可以直接在java中调用,像python那样。在写此文章的时候,搜到了其他解决方案,JNA和JNative,可以简化开发。