`
爱我靠边站
  • 浏览: 11905 次
  • 性别: Icon_minigender_2
  • 来自: 北京
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

如何在Java中调用dll文件

阅读更多

如何在Java中调用dll文件

我知道的有两种方法,一种是直接用JNI,另一种是则是用Jacob(实质上也用的是JNI)

先讲讲第一种方法

1.编写java程序TestDll,注意,这个类有两个作用,一个是用来做头文件,另外一个作用就是通过它来调用dll
public class TestDll {
static
{
System.loadLibrary("DLLSample");//载入dll
}
public native static int DoubleValue(int i);//函数声明
}

2.编译: javac TestDll
3.生成头文件: javah TestDll
生成TestDll.h文件,这里面只对函数DoubleValue作了声明
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h></jni.h>
/* Header for class TestDll */

#ifndef _Included_TestDll
#define _Included_TestDll
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestDll
* Method: DoubleValue
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_TestDll_DoubleValue
(JNIEnv *, jclass, jint);

#ifdef __cplusplus
}
#endif
#endif


4.用vc制作DLL
修改DLLSample工程,程序中添加函数DoubleValue的实现,函数名必须用jni规定格式,可以照到.h文件里的声

明来写:

#include "jni_md.h"
#include "TestDll.h"

JNIEXPORT jint JNICALL Java_TestDll_DoubleValue (JNIEnv *, jclass, jint p)
{
int j = p*2;
return j;

JNIEXPORT,JNICALL,JNIEnv *, jclass请都不要动,jint对应是java里的int

别忘了把TestDll.h,jni.h(这个文件在jdk/include下),jni_md.h(这个文件在jdk/include/win32下)复制到VC

工程目录中,然后编译生成dll

5.把生成的DLLSample.dll复制到jdk/bin下(确保PATH指向了这个目录)

6.应用
我看到他们举的例子都是把调用写在了main里, 在这里我把应用取出来放在一个servlet类中的测试的,同样可

以得到正确结果,这里体现国TestDll的第二个作用,它是调用dll的一个纽带
......
TestDll td = new TestDll();
System.out.println("result= "+td.DoubleValue(25));
.....


注意:
如果更改了TestDll的内容或重使命名了这个文件,必须重新用它生成头文件,在dll里也要做相应修改,

因为必须要和java里面的声明一致才行

分享到:
评论
9 楼 protti 2007-10-16  
唉   关于运行dll文件  发现网上都是需要嵌套C的代码  有没有纯java搞出来的吖
8 楼 protti 2007-10-16  
唉   关于运行dll文件  发现网上都是需要嵌套C的代码  有没有纯java搞出来的吖
7 楼 protti 2007-10-16  
唉   关于运行dll文件  发现网上都是需要嵌套C的代码  有没有纯java搞出来的吖
6 楼 抛出异常的爱 2007-04-24  
爱我靠边站 写道
谢谢忠告,本没打算有人会细看的。
下次注意,我会写些自己开发的代码的。
主要是要把代码用代码框再框一次。。。
没格式的代码现在已经看不明白了。。。
5 楼 爱我靠边站 2007-04-24  
谢谢忠告,本没打算有人会细看的。
下次注意,我会写些自己开发的代码的。
4 楼 抛出异常的爱 2007-04-24  
转的时候要再排排版。。。。
3 楼 giscat 2007-04-24  
这两天刚好整这玩样,搞得哭死
2 楼 爱我靠边站 2007-04-24  
JNI完全手册

JNI完全手册(一) yippit 原创
最近在公司里做了一个手机的项目,需要JAVA程序在发送短信的时候和第三方的短信服务器连接。短信接口是用C++写的。琢磨了三天,大致搞懂了JNI的主体部分。先将心得整理,希望各位朋友少走弯路。
首先引用一篇文章,介绍一个简单的JNI的调用的过程。
JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能。解决JAVA对本地操作的一种方法就是JNI。
JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式)。通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。
简单介绍及应用如下:
一、JAVA中所需要做的工作
在JAVA程序中,首先需要在类中声明所调用的库名称,如下:


static {
System.loadLibrary(“goodluck”);
}

在这里,库的扩展名字可以不用写出来,究竟是DLL还是SO,由系统自己判断。
还需要对将要调用的方法做本地声明,关键字为native。并且只需要声明,而不需要具 体实现。如下:
public native static void set(int i);
public native static int get();
然后编译该JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就会生成C/C++的头文件。
例如程序testdll.java,内容为:

public class testdll
{
static
{
System.loadLibrary("goodluck");
}
public native static int get();
public native static void set(int i);
public static void main(String[] args)
{
testdll test = new testdll();
test.set(10);
System.out.println(test.get());
}
}

用javac testdll.java编译它,会生成testdll.class。
再用javah testdll,则会在当前目录下生成testdll.h文件,这个文件需要被C/C++程序调用来生成所需的库文件。
二、C/C++中所需要做的工作
对于已生成的.h头文件,C/C++所需要做的,就是把它的各个方法具体的实现。然后编译连接成库文件即可。再把库文件拷贝到JAVA程序的路径下面,就可以用JAVA调用C/C++所实现的功能了。
接上例子。我们先看一下testdll.h文件的内容:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class testdll */
#ifndef _Included_testdll
#define _Included_testdll
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: testdll
* Method: get
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass);
/*
* Class: testdll
* Method: set
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif
在具体实现的时候,我们只关心两个函数原型
JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass); 和
JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);
这里JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jint是以JNI为中介使JAVA的int类型与本地的int沟通的一种类型,我们可以视而不见,就当做int使用。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数中,我们也只需要关心在JAVA程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。
好,下面我们用testdll.cpp文件具体实现这两个函数:
#include "testdll.h"
int i = 0;
JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass)
{
return i;
}
JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint j)
{
i = j;
}
编译连接成库文件,本例是在WINDOWS下做的,生成的是DLL文件。并且名称要与JAVA中需要调用的一致,这里就是goodluck.dll 。把goodluck.dll拷贝到testdll.class的目录下,java testdll运行它,就可以观察到结果了。
我的项目比较复杂,需要调用动态链接库,这样在JNI传送参数到C程序时,需要对参数进行处理转换。才可以被C程序识别。
大体程序如下:


public class SendSMS {
static
{
System.out.println(System.getProperty("java.library.path"));
System.loadLibrary("sms");
}
public native static int SmsInit();
public native static int SmsSend(byte[] mobileNo, byte[] smContent);
}

在这里要注意的是,path里一定要包含类库的路径,否则在程序运行时会抛出异常:
java.lang.UnsatisfiedLinkError: no sms in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1491)
at java.lang.Runtime.loadLibrary0(Runtime.java:788)
at java.lang.System.loadLibrary(System.java:834)
at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14)
at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)
Exception in thread "main"
指引的路径应该到.dll文件的上一级,如果指到.dll,则会报:
java.lang.UnsatisfiedLinkError: C:\sms.dll: Can't find dependent libraries
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1560)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1485)
at java.lang.Runtime.loadLibrary0(Runtime.java:788)
at java.lang.System.loadLibrary(System.java:834)
at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14)
at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)
Exception in thread "main"
通过编译,生成com_mobilesoft_sms_mobilesoftinfo_SendSMS.h头文件。(建议使用Jbuilder进行编译,操作比较简单!)这个头文件就是Java和C之间的纽带。要特别注意的是方法中传递的参数jbyteArray,这在接下来的过程中会重点介绍。


/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_mobilesoft_sms_mobilesoftinfo_SendSMS */
#ifndef _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS
#define _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS
* Method: SmsInit
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit
(JNIEnv *, jclass);
/*
* Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS
* Method: SmsSend
* Signature: ([B[B)I
*/
JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend
(JNIEnv *, jclass, jbyteArray, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif

对于我要调用的C程序的动态链接库,C程序也要提供一个头文件,sms.h。这个文件将要调用的方法罗列了出来。


/*
* SMS API
* Author: yippit
* Date: 2004.6.8
*/
#ifndef MCS_SMS_H
#define MCS_SMS_H
#define DLLEXPORT __declspec(dllexport)
/*sms storage*/
#define SMS_SIM 0
#define SMS_MT 1
/*sms states*/
#define SMS_UNREAD 0
#define SMS_READ 1
/*sms type*/
#define SMS_NOPARSE -1
#define SMS_NORMAL 0
#define SMS_FLASH 1
#define SMS_MMSNOTI 2
typedef struct tagSmsEntry {
int index; /*index, start from 1*/
int status; /*read, unread*/
int type; /*-1-can't parser 0-normal, 1-flash, 2-mms*/
int storage; /*SMS_SIM, SMS_MT*/
char date[24];
char number[32];
char text[144];
} SmsEntry;
DLLEXPORT int SmsInit(void);
DLLEXPORT int SmsSend(char *phonenum, char *content);
DLLEXPORT int SmsSetSCA(char *sca);
DLLEXPORT int SmsGetSCA(char *sca);
DLLEXPORT int SmsSetInd(int ind);
DLLEXPORT int SmsGetInd(void);
DLLEXPORT int SmsGetInfo(int storage, int *max, int *used);
DLLEXPORT int SmsSaveFlash(int flag);
DLLEXPORT int SmsRead(SmsEntry *entry, int storage, int index);
DLLEXPORT int SmsDelete(int storage, int index);
DLLEXPORT int SmsModifyStatus(int storage, int index); /*unread -> read*/
#endif

在有了这两个头文件之后,就可以进行C程序的编写了。也就是实现对JNI调用的两个方法。在网上的资料中,由于调用的方法实现的都比较简单,(大多是打印字符串等)所以避开了JNI中最麻烦的部分,也是最关键的部分,参数的传递。由于Java和C的编码是不同的,所以传递的参数是要进行再处理,否则C程序是会对参数在编译过程中提出警告,例如;warning C4024: 'SmsSend' : different types for formal and actual parameter 2等。
Sms.c的程序如下:


#include "sms.h"
#include "com_mobilesoft_sms_mobilesoftinfo_SendSMS.h"
JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit(JNIEnv * env, jclass jobject)
{
return SmsInit();
}

JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend(JNIEnv * env, jclass jobject, jbyteArray mobileno, jbyteArray smscontent)
{
char * pSmscontent ;
//jsize theArrayLengthJ = (*env)->GetArrayLength(env,mobileno);
jbyte * arrayBody = (*env)->GetByteArrayElements(env,mobileno,0);
char * pMobileNo = (char *)arrayBody;
printf("[%s]\n ", pMobileNo);
//jsize size = (*env)->GetArrayLength(env,smscontent);
arrayBody = (*env)->GetByteArrayElements(env,smscontent,0);
pSmscontent = (char *)arrayBody;
printf("<%s>\n", pSmscontent);
return SmsSend(pMobileNo,pSmscontent);
}

对于C或C++,在程序上是会有稍微的不同,这可以由读者对其进行适当的修改。这里要注意的是GetArrayLength,GetByteArrayElements等这些JNI中已经包含的方法,这些方法是专门对转换参数类型而提供的。具体的方法有很多,在下一篇中会做专门的介绍。
在完成了上述的文件后,可以对sms.c进行编译,生成.dll文件(建议在release中编译,这样动态链接库的容积会比较小!)
完成.dll文件的编译后,就可以在Java中调用C程序中的方法了。例如文件test.java


public class test {
public test() {
}
public static void main(String[] args) {
byte[] mobileno = {
0x31, 0x33, 0x36, 0x36, 0x31, 0x36, 0x33, 0x30, 0x36, 0x36, 0x37, 0x00};
String smscontentemp = "早上好";
byte[] temp = {0};
try {
byte[] smscontentdb = smscontentemp.getBytes("gbk");
byte[] smscontent = new byte[smscontentdb.length + temp.length];
System.arraycopy(smscontentdb, 0, smscontent, 0, smscontentdb.length);
System.arraycopy(temp, 0, smscontent, smscontentdb.length, temp.length);
SendSMS sendSMS = new SendSMS();
sendSMS.SmsInit();
if (sendSMS.SmsSend(mobileno, smscontent) >= 0) {
System.out.println("chenggong !");
}
else {
System.out.println("shibai !");
}
}catch (Exception ex) {}
}
}

在这个文件中要注意的有一点,就是在传递字节数组到C程序中时,最后的结尾一定要以0结束。这是一个偷懒的做法,不过是个有效的做法。因为大多数情况下,接口是由第三方提供的。所以我们一般是不知道在C的方法里,具体是怎么处理参数的。而C又是要求数组是有长度。所以,在Java中,如果你不想写程序传数组的长度,那么在数组中以0结尾就是最方便的方法了。当然,如果有更好的方法也希望大家提出。
到这里,一个完整的Java通过JNI调用动态链接库的程序就完成了。实际上也不是很复杂。只要多注意一下细节,是很容易得出来的。

我的项目比较复杂,需要调用动态链接库,这样在JNI传送参数到C程序时,需要对参数进行处理转换。才可以被C程序识别。
大体程序如下:

public class SendSMS {
static
{
System.out.println(System.getProperty("java.library.path"));
System.loadLibrary("sms");
}
public native static int SmsInit();
public native static int SmsSend(byte[] mobileNo, byte[] smContent);
}

在这里要注意的是,path里一定要包含类库的路径,否则在程序运行时会抛出异常:
java.lang.UnsatisfiedLinkError: no sms in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1491)
at java.lang.Runtime.loadLibrary0(Runtime.java:788)
at java.lang.System.loadLibrary(System.java:834)
at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14)
at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)
Exception in thread "main"
指引的路径应该到.dll文件的上一级,如果指到.dll,则会报:
java.lang.UnsatisfiedLinkError: C:\sms.dll: Can't find dependent libraries
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1560)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1485)
at java.lang.Runtime.loadLibrary0(Runtime.java:788)
at java.lang.System.loadLibrary(System.java:834)
at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14)
at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)
Exception in thread "main"
通过编译,生成com_mobilesoft_sms_mobilesoftinfo_SendSMS.h头文件。(建议使用Jbuilder进行编译,操作比较简单!)这个头文件就是Java和C之间的纽带。要特别注意的是方法中传递的参数jbyteArray,这在接下来的过程中会重点介绍。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_mobilesoft_sms_mobilesoftinfo_SendSMS */
#ifndef _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS
#define _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS
* Method: SmsInit
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit
(JNIEnv *, jclass);
/*
* Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS
* Method: SmsSend
* Signature: ([B[B)I
*/
JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend
(JNIEnv *, jclass, jbyteArray, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif

对于我要调用的C程序的动态链接库,C程序也要提供一个头文件,sms.h。这个文件将要调用的方法罗列了出来。

/*
* SMS API
* Author: yippit
* Date: 2004.6.8
*/
#ifndef MCS_SMS_H
#define MCS_SMS_H
#define DLLEXPORT __declspec(dllexport)
/*sms storage*/
#define SMS_SIM 0
#define SMS_MT 1
/*sms states*/
#define SMS_UNREAD 0
#define SMS_READ 1
/*sms type*/
#define SMS_NOPARSE -1
#define SMS_NORMAL 0
#define SMS_FLASH 1
#define SMS_MMSNOTI 2
typedef struct tagSmsEntry {
int index; /*index, start from 1*/
int status; /*read, unread*/
int type; /*-1-can't parser 0-normal, 1-flash, 2-mms*/
int storage; /*SMS_SIM, SMS_MT*/
char date[24];
char number[32];
char text[144];
} SmsEntry;
DLLEXPORT int SmsInit(void);
DLLEXPORT int SmsSend(char *phonenum, char *content);
DLLEXPORT int SmsSetSCA(char *sca);
DLLEXPORT int SmsGetSCA(char *sca);
DLLEXPORT int SmsSetInd(int ind);
DLLEXPORT int SmsGetInd(void);
DLLEXPORT int SmsGetInfo(int storage, int *max, int *used);
DLLEXPORT int SmsSaveFlash(int flag);
DLLEXPORT int SmsRead(SmsEntry *entry, int storage, int index);
DLLEXPORT int SmsDelete(int storage, int index);
DLLEXPORT int SmsModifyStatus(int storage, int index); /*unread -> read*/
#endif

在有了这两个头文件之后,就可以进行C程序的编写了。也就是实现对JNI调用的两个方法。在网上的资料中,由于调用的方法实现的都比较简单,(大多是打印字符串等)所以避开了JNI中最麻烦的部分,也是最关键的部分,参数的传递。由于Java和C的编码是不同的,所以传递的参数是要进行再处理,否则C程序是会对参数在编译过程中提出警告,例如;warning C4024: 'SmsSend' : different types for formal and actual parameter 2等。
Sms.c的程序如下:

#include "sms.h"
#include "com_mobilesoft_sms_mobilesoftinfo_SendSMS.h"
JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit(JNIEnv * env, jclass jobject)
{
return SmsInit();
}

JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend(JNIEnv * env, jclass jobject, jbyteArray mobileno, jbyteArray smscontent)
{
char * pSmscontent ;
//jsize theArrayLengthJ = (*env)->GetArrayLength(env,mobileno);
jbyte * arrayBody = (*env)->GetByteArrayElements(env,mobileno,0);
char * pMobileNo = (char *)arrayBody;
printf("[%s]\n ", pMobileNo);
//jsize size = (*env)->GetArrayLength(env,smscontent);
arrayBody = (*env)->GetByteArrayElements(env,smscontent,0);
pSmscontent = (char *)arrayBody;
printf("<%s>\n", pSmscontent);
return SmsSend(pMobileNo,pSmscontent);
}

对于C或C++,在程序上是会有稍微的不同,这可以由读者对其进行适当的修改。这里要注意的是GetArrayLength,GetByteArrayElements等这些JNI中已经包含的方法,这些方法是专门对转换参数类型而提供的。具体的方法有很多,在下一篇中会做专门的介绍。
在完成了上述的文件后,可以对sms.c进行编译,生成.dll文件(建议在release中编译,这样动态链接库的容积会比较小!)
完成.dll文件的编译后,就可以在Java中调用C程序中的方法了。例如文件test.java

public class test {
public test() {
}
public static void main(String[] args) {
byte[] mobileno = {
0x31, 0x33, 0x36, 0x36, 0x31, 0x36, 0x33, 0x30, 0x36, 0x36, 0x37, 0x00};
String smscontentemp = "早上好";
byte[] temp = {0};
try {
byte[] smscontentdb = smscontentemp.getBytes("gbk");
byte[] smscontent = new byte[smscontentdb.length + temp.length];
System.arraycopy(smscontentdb, 0, smscontent, 0, smscontentdb.length);
System.arraycopy(temp, 0, smscontent, smscontentdb.length, temp.length);
SendSMS sendSMS = new SendSMS();
sendSMS.SmsInit();
if (sendSMS.SmsSend(mobileno, smscontent) >= 0) {
System.out.println("chenggong !");
}
else {
System.out.println("shibai !");
}
}catch (Exception ex) {}
}
}

在这个文件中要注意的有一点,就是在传递字节数组到C程序中时,最后的结尾一定要以0结束。这是一个偷懒的做法,不过是个有效的做法。因为大多数情况下,接口是由第三方提供的。所以我们一般是不知道在C的方法里,具体是怎么处理参数的。而C又是要求数组是有长度。所以,在Java中,如果你不想写程序传数组的长度,那么在数组中以0结尾就是最方便的方法了。当然,如果有更好的方法也希望大家提出。
到这里,一个完整的Java通过JNI调用动态链接库的程序就完成了。实际上也不是很复杂。只要多注意一下细节,是很容易得出来的。

1 楼 爱我靠边站 2007-04-24  
如何用JNI技术提高Java的性能详解
阻碍Java获得广泛应用的一个主要因素是Java程序的运行效率。Java是介于解释型和编译型之间的一种语言,同样的程序,如果用编译型语言C来实现,其运行速度一般要比Java快一倍以上。Java具有平台无关性,这使人们在开发企业级应用的时候总是把它作为主要候选方案之一,但是性能方面的因素又大大削弱了它的竞争力。为此,提高Java的性能就显得十分重要。

问题的提出

Sun公司及Java的支持者们为提高Java的运行速度已经做出了许多努力,其中大多数集中在程序设计的方法和模式选择方面。由于算法和设计模式的优化是通用的,对Java有效的优化算法和设计模式,对其他编译语言也基本同样适用,因此不能从根本上改变Java程序与编译型语言在执行效率方面的差异。

JIT(Just In Time,及时编译)技术是个比较好的思想。它的基本原理是:首先通过Java编译器把Java源代码编译成平台无关的二进制字节码。然后在Java程序真正执行之前,系统通过JIT编译器把Java的字节码编译为本地化机器码。最后,系统执行本地化机器码,节省了对字节码进行解释的时间。这样做的优点是大大提高了Java程序的性能,缩短了加载程序的时间;同时,由于编译的结果并不在程序运行间保存,因此也节约了存储空间。缺点是由于JIT编译器对所有的代码都想优化,因此同样也占用了很多时间。

动态优化技术是提高Java性能的另一个尝试。该技术试图通过把Java源程序直接编译成机器码,以充分利用Java动态编译和静态编译技术来提高Java的性能。该方法把输入的Java源码或字节码转换为经过高度优化的可执行代码和动态库 (Windows中的. dll文件或Unix中的. so文件)。该技术能大大提高程序的性能,但却破坏了Java的可移植性。

JNI技术

实际上,有一种通常为我们忽视的技术可以在很大程度上解决这个难题,那就是JNI(Java Native Interface, Java本地化方法)。主张采用纯Java的人们通常反对本地化代码的使用,他们认为在Java程序执行的过程中调用C/C++程序会影响程序的可移植性和安全性。还有一些人认为JNI只是对过去混合编程技术的简单扩展,其实际目的是为了充分利用大量原有的C程序库。

其实,我们不必拘泥于严格的平台独立性限制,因为采用JNI技术只是针对一些严重影响Java性能的代码段,该部分可能只占源程序的极少部分,所以几乎可以不考虑该部分代码在主流平台之间移植的工作量。同时,也不必过分担心类型匹配问题,我们完全可以控制代码不出现这种错误。此外,也不必担心安全控制问题,因为Java安全模型已扩展为允许非系统类加载和调用本地方法。根据Java规范,从JDK 1. 2开始,FindClass将设法找到与当前的本地方法关联的类加载器。如果平台相关代码属于一个系统类,则无需涉及任何类加载器; 否则,将调用适当的类加载器来加载和链接已命名的类。换句话说,如果在Java程序中直接调用C/C++语言产生的机器码,该部分代码的安全性就由Java虚拟机控制。

JNI实现步骤

编写JNI代码的大致流程如下图所示:



JNI实现流程图

1. 首先编写需要JNI功能的Java类源文件。其中,需要JNI实现的方法应当用native关键字声明。在该类中,用System. loadLibrary()方法加载需要的动态链接库。关键代码如下:

//Compute.java

……

public class Compute {

public native double comp (double [] params);

……

static {

// 调用动态链接库

System. loadLibrary(“mathlib”);

}

……

}

2. 将该类源文件用Java类编译器编译成二进制字节码文件。由于采用了native关键字声明,编译器会忽视没有代码体的JNI方法部分。

3. 利用javah -jni *.class 生成相关JNI方法的头文件。我们可以手工生成该文件,但是由于Java虚拟机是根据一定的命名规范完成对JNI方法的调用,所以手工编写头文件需要特别小心。

上述文件产生的头文件部分代码如下:

//Compute. h

……

extern “C” {

JNIEXPORT jdouble JNICALL Java_Compute_comp (JNIEnv *, jobject, jdoubleArray);

}

……

可以看出,JNI函数名称分为三部分:首先是Java关键字,供Java虚拟机识别;然后是调用者类名称(全限定的类名,其中用下划线代替名称分隔符);最后是对应的方法名称,各段名称之间用下划线分割。

JNI函数的参数也由三部分组成: 首先是JNIEnv *,是一个指向JNI运行环境的指针;第二个参数随本地方法是静态还是非静态而有所不同——非静态本地方法的第二个参数是对对象的引用,而静态本地方法的第二个参数是对其 Java 类的引用; 其余的参数对应通常 Java 方法的参数,参数类型需要根据一定规则进行映射。

4. 根据头文件编写相应方法的实现代码。由于篇幅所限,具体的实现部分在此不再赘述。在编码过程中,需要注意变量的长度问题,例如Java的整型变量长度为32位,而C语言为16位,所以要仔细核对变量类型映射表,防止在传值过程中出现问题。

5. 利用C/C++编译器将JNI实现代码编译成动态链接库。调用者类中需要显式调用该链接库。

在Win32环境下,可以利用Visual C ++或其他能产生DLL文件的C/C++编译器将实现代码编译成动态链接库。笔者利用的是Microsoft.NET Framework的编译器。编译指令如下,其中%Java_HOME%是笔者的jdk安装目录变量:

cl -I%Java_HOME%\include

-I%Java_HOME%\include\win32

-LD jnicomp. c -Femathlib. dll

在Sun Soloaris下,相应指令为:

cc -G -I/usr/local/java/include -I/usr/local/java/include/solaris jnicomp. c \

-o mathlib. so

注意,编译的时候需要用I指令包含必要的库文件路径。

经过上述处理,就基本上完成了一个包含本地化方法的Java类的开发。

JNI技术的应用

一些主要的Java技术,如JDBC和RMI,大部分都采用JNI方式实现。但是,采用JNI确实会影响程序的平台无关性,所以只能在特别需要的地方才能使用。通常来说,如果遇到下面的情况,我们可以考虑JNI:

● 需要直接操作物理设备,而没有相关的驱动程序,这时候我们可能需要用C甚至汇编语言来编写该设备的驱动,然后通过JNI调用;

● 涉及大量数学运算的部分,用Java会带来些效率上的损失;

● 用Java会产生系统难以支付的开销,如需要大量网络链接的场合;

● 存在大量可重用的C/C++代码,通过JNI可以减少开发工作量,避免重复开发。

另外,在利用JNI技术的时候要注意以下几点:

● 由于Java安全机制的限制,不要试图通过Jar文件的方式发布包含本地化方法的Applet到客户端;

● 注意内存管理问题,虽然在本地方法返回 Java 后将自动释放局部引用,但过多的局部引用将使虚拟机在执行本地方法时耗尽内存;

● JNI技术不仅可以让Java程序调用C/C++代码,也可以让C/C++代码调用Java代码。

相关推荐

    JAVA调用DLL方法 JAVA调用DLL方

    通过本文的介绍,我们可以看到Java调用DLL文件不仅可行,而且有多种方式可以选择。其中,JNative提供了一种简单直接的方法,大大降低了调用本地库的难度。对于那些希望利用Java的强大功能同时又需要调用特定DLL文件...

    java dll文件调用的几种方式

    下面将详细介绍几种在Java中调用DLL文件的方法,并提供一些实践中的注意事项。 1. **Java Native Interface (JNI)**: JNI是Java平台标准的一部分,它允许Java代码和其他语言写的代码进行交互。要使用JNI调用DLL,...

    亲测可用,java 成功调用dll函数。包含调用回调函数,springboot版本。最近由于公司业务需要,要调用dll文件,用JNA调用。

    Java调用DLL函数是跨平台编程中的一种常见需求,特别是在Java与C/C++代码交互时。JNA(Java Native Access)是Java平台上的一个库,它允许Java代码直接调用本机库(如DLL文件)的函数,而无需编写JNI(Java Native ...

    java调用DLL文件

    总结来说,Java调用DLL文件主要依赖于JNative库,通过定义Java接口、加载DLL、获取函数句柄、创建代理对象以及调用函数等步骤实现。这种方法使得Java开发者能够充分利用C/C++库的功能,同时也保持了Java的跨平台特性...

    在Java程序中使用JNative调用dll文件

    调用DLL文件的过程通常包括以下几个步骤: 1. **创建DLL**:首先,你需要使用C或C++编写一个DLL文件,其中包含将被Java调用的函数。这些函数需要按照JNI的规范来编写,即使用`JNIEXPORT`和` JNICALL`宏,以及指定...

    几行代码轻松搞定 java 调用 dll 文件中的函数

    ### Java调用DLL文件中的函数 #### 知识点概览 本文将详细介绍如何在Java环境中调用DLL(动态链接库)文件中的函数,并通过一个具体的示例来演示这一过程。主要内容包括: - Java Native Access (JNA) 库的介绍与...

    java通过 jawin和jacob 调用dll文件

    本篇文章将详细探讨如何使用JAWIN和JCOB这两个库在Java中调用DLL文件。 JAWIN(Java to Windows Interface)是一个开源项目,它的主要目标是提供一种方式,使得Java程序能够直接与Windows API进行交互。JAWIN通过...

    java调用dll文件的实例

    Java调用DLL文件是将Java程序与C/C++编写的动态链接库集成的一种技术,主要通过JNI(Java Native Interface)来实现。JNI是Java平台的标准部分,它允许Java代码和其他语言写的代码进行交互。在本实例中,我们将探讨...

    java调用C#封装的dll方法

    - **实现Java调用:** 在`COMTest_Start`类中实现调用C++ DLL的方法。 **技术要点:** - 需要注意C#和Java中的数据类型转换问题,特别是字符串类型的处理。 - 确保C++项目启用了CLR支持。 - JNA的配置与使用。 ##...

    Java调用dll文件开源组件jar包及说明文档

    Java调用DLL文件是软件开发中的常见需求,尤其是在Java与C/C++混合编程时。`JNative-1.3.2`是一个开源组件,专门设计用于帮助Java应用程序调用Windows平台上的DLL(动态链接库)文件,同时也支持Linux平台下的SO...

    非常好用java调用c++ dll文件demo

    5. **调用DLL方法**:现在,Java程序可以通过JNI接口调用DLL文件中的函数。 在提供的"java_dll_sample"压缩包中,可能包含以下元素: - `Java调用C++ DLL的Java类`:这个类会声明`native`方法,并在运行时通过`...

    Java调用dll实现des加密解密

    总的来说,通过Java调用DLL实现DES加密解密涉及了Java和C/C++的混合编程,需要理解JNI的工作原理,以及如何在C/C++中与DLL交互。这个过程对于那些希望在Java中利用已有的本地库功能的开发者来说是至关重要的。

    Java调用Qt DLL

    在本场景中,JNI是Java调用Qt DLL的关键桥梁。通过JNI,Java应用可以加载动态链接库(DLL),并调用其中的函数。 2. **Qt库的动态链接**:在Windows系统中,Qt库通常以DLL形式存在。要从Java程序中调用Qt功能,首先...

    Java调用动态链接库(Java 调用 DLL)

    在实际项目中,Java调用DLL通常用于利用已有的C/C++库,或者执行一些Java不擅长的任务,如硬件控制、图形处理等。然而,由于涉及到本地代码,这也会增加程序的复杂性和维护难度,因此在选择这种方法时需要权衡利弊。...

    JAVA调用C#写的DLL例子

    在本篇文章中,我们将详细介绍如何使用 JAVA 调用 C# 编写的 DLL 文件。首先,我们需要了解什么是 DLL 文件,以及它在编程中的应用。 DLL 文件是一种动态链接库文件,它可以被多个程序同时使用,减少了代码的重复...

    Java调用Labview生成的DLL文件

    1、Labview生成Dll文件 2、Java通过Jnative第三方Jar包调用Labview生成的DLL文件 3、Java在Windows系统调用Labview生成的Dll的说明 有JNative的使用说明 直接下载,根据使用说明可以直接使用。

    java调用dll最简单的方法

    在Java中调用DLL(Dynamic Link Library)文件,可以使用Java的本地方法接口(JNI,Java Native Interface)来实现。JNI允许Java程序与本地代码进行交互,从而实现跨平台的功能。

    JAVA JNI调用DLL完整步骤

    总结,Java调用DLL的完整步骤包括:定义Java的`native`方法,使用`javah`生成头文件,编写C/C++ DLL代码,编译DLL,最后在Java程序中调用。这个过程可能需要对JNI规范有深入理解,以及一定的C/C++编程基础。通过熟练...

    JAVA如何调用dll:用JNI调用C或C++动态联接库原来如此简单

    - DLL的路径:确保Java能够找到DLL文件,可能需要设置系统路径或者将DLL放在Java的类路径下。 - 平台兼容性:JNI和DLL是平台相关的,Windows下的DLL不能直接在Linux或Mac上使用。 - 错误处理:在C/C++代码中加入...

    java调用ocx控件,jacob,jar和dll文件

    在Java后端开发中,Apache的HTTP服务器、Tomcat应用服务器,或者其他相关的开源组件可能会与Java调用OCX控件的场景相结合。 总之,通过Jacob库,Java开发者可以克服平台限制,实现与Windows OCX控件的交互。这在...

Global site tag (gtag.js) - Google Analytics