网上科普有关“java 加载动态链接库怎么使用相对路径”话题很是火热,小编也是针对java 加载动态链接库怎么使用相对路径寻找了一些与之相关的一些信息进行分析,如果能碰巧解决你现在面临的问题,希望能够帮助到您。
对于java程序员来说,java语言的好处和优点,我想不用我说了,大家自然会说出很多一套套的。但虽然我们作为java程序员,但我们不得不承认java语言也有一些它本身的缺点。比如在性能、和底层打交道方面都有它的缺点。所以java就提供了一些本地接口,他主要的作用就是提供一个标准的方式让java程序通过虚拟机与原生代码进行交互,这也就是我们平常常说的java本地接口(JNI——java native Interface)。它使得在 Java 虚拟机(VM) 内部运行的Java 代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行互操作。JNI 最重要的好处是它没有对底层 Java 虚拟机的实现施加任何限制。因此,Java虚拟机厂商可以在不影响虚拟机其它部分的情况下添加对JNI 的支持。程序员只需编写一种版本的本地应用程序或库,就能够与所有支持JNI 的Java 虚拟机协同工作。我们来看一下为什么要与原生代码进行交互:
一:提高应用程序性能。我们知道java对于c/c++、汇编语言来说,显得比较“高级”。其实这里的高级就是简化了程序员的工作。很多底层的东西都让java虚拟机做了。但毕竟相对于直接访问底层来讲,java多了一步虚拟机的过程,所以在性能上比着这些原生语言稍微有点慢。
二:实现一些与底层相关的功能。Java平台提供的标准类库,还有强大的API,虽然能完成大部分功能。但有些和底层硬件打交道的功能在java API提供的类库中还是无法完成。
三:与已有的使用原生代码编写的程序进行集成。在于操作系统上由c或者c++等原生语言编写的软件进行集成的时候,可以用JNI。
JNI 接口函数和指针
平台相关代码是通过调用 JNI 函数来访问Java 虚拟机功能的。JNI 函数可通过接口指针来获得。接口指针是指针的指针,它指向一个指针数组,而指针数组中的每个元素又指向一个接口函数。每个接口函数都处在数组的某个预定偏移量中。下图说明了接口指针的组织结构。
JNI 接口的组织类似于C++ 虚拟函数表或COM 接口。使用接口表而不使用硬性编入的函数表的好处是使JNI 名字空间与平台相关代码分开。虚拟机可以很容易地提供多个版本的JNI 函数表。例如,虚拟机可支持以下两个JNI 函数表:
1)一个表对非法参数进行全面检查,适用于调试程序;
2)另一个表只进行 JNI 规范所要求的最小程度的检查,因此效率较高。
JNI 接口指针只在当前线程中有效。因此,本地方法不能将接口指针从一个线程传递到另一个线程中。实现 JNI 的虚拟机可将本地线程的数据分配和储存在 JNI 接口指针所指向的区域中。
本地方法将JNI 接口指针当作参数来接受。虚拟机在从相同的 Java 线程中对本地方法进行多次调用时,保证传递给该本地方法的接口指针是相同的。但是,一个本地方法可被不同的 Java 线程所调用,因此可以接受不同的 JNI 接口指针。
1)编写Java类代码
其中,需要JNI实现的方法应当用native关键字声明,在该类中,用System.loadLibrary()方法加载需要的动态链接库,关键代码如下:
//Compute.java
public class Compute{
public native double sqrt(double params);
static{
//调用动态链接库
System.loadLibrary("compute");
}
}
2)编译成字节代码
在这个过程中,由于采用了native关键字声明,Java编译器会忽视没有代码体的JNI方法部分。
3)生成相关JNI方法的头文件
这个过程的实现一般是通过利用jlavah-jni * class生成的(-jni可以省略),也可以手工生成该文件;但是由于 Java 虚拟机是根据一定的命名规范完成对JNI方法的调用,所以手工编写头文件需要特别小心。
上述文件产生的头文件部分代码如下:
//Compute.h
extern“C”{
JNIEXPORT jdoubleJNICALL Java_Compute_comp(JNI-Env *, jobject, jdoubleArray);
JNI函数名称分为三部分:首先是Java关键字,供Java虚拟机识别;然后是调用者类名称(全限定的类名,其中用下划线代替名称分隔符);最后是对应的方法名称,各段名称之间用下划线分割。
JNI函数的参数也由三部分组成:首先是JNIEnv *,是一个指向JNI运行环境的指针;第二个参数随本地方法是静态还是非静态而有所不同一一非静态本地方法的第二个参数是对对象的引用,而静态本地方法的第二个参数是对其Java类的引用;其余的参数对应通常Java方法的参数,参数类型需要根据一定规则进行映射。
4)编写相应方法的实现代码
在编码过程中,需要注意变量的长度问题,例如Java的整型变量长度为32位,而C语言为16位,所以要仔细核对变量类型映射表,防止在传值过程中出现问题。
5)将JNI实现代码编译成动态链接库
编译过程是利用C/C++编译器实现的,在windows平台上,编译和连接的结果是动态链接库DLL文件。当要使用生成的动态链接库时,调用者类中需要显式调用该链接库dll文件。
经过上述处理,基本上完成了一个包含本地化方法的Java类的开发。
附录:将Jav类型映射到本地C 类型
为了使用方便,特提供以下定义。
#define JNI_FALSE 0
#define JNI_TRUE 1
jsize 整数类型用于描述主要指数和大小:
typedef jint jsize;
故障排除
当使用 JNI 从Java 程序访问本机代码时,您会遇到许多问题。您会遇到的三个最常见的错误是:
1)无法找到动态链接。它所产生的错误消息是:java.lang.UnsatisfiedLinkError。这通常指无法找到共享库,或者无法找到共享库内特定的本机方法。
2)无法找到共享库文件。当用 System.loadLibrary(String libname) 方法(参数是文件名)装入库文件时,请确保文件名拼写正确以及没有指定扩展名。还有,确保库文件的位置在类路径中,从而确保 JVM 可以访问该库文件。
3)无法找到具有指定说明的方法。确保您的 C/C++ 函数实现拥有与头文件中的函数说明相同的说明。
结束语
从 Java 调用 C 或 C++ 本机代码(虽然不简单)是 Java 平台中一种良好集成的功能。虽然 JNI 支持 C 和 C++,但 C++ 接口更清晰一些并且通常比 C 接口更可取。正如您已经看到的,调用 C 或 C++ 本机代码需要赋予函数特殊的名称,并创建共享库文件。当利用现有代码库时,更改代码通常是不可取的。要避免这一点,在C++ 中,通常创建代理代码或代理类,它们有专门的 JNI 所需的命名函数。然后,这些函数可以调用底层库函数,这些库函数的说明和实现保持不变。
在JAVA中如何得到一个文件,文件夹的创建时间?
最近在做一个项目的时候,遇到一个要在java程序中改变linux的PATH环境变量的问题。我们知道在linux中PATH环境变量保存在用户根目录下的“.bashrc”和“.bash_profile这两个隐藏文件中。用户登录的过程中便会把这两个文件中的PATH路径记录的该用户的shell中。如果用户已经登录,这时可通过命令 export PATH=add_path:$PATH来增加一个路径为add_path的路径。但通过此种方式增加的PATH路径只在当前shell中有效(也就是说,当用户通过另一个shell登录时,PATH又变成了原来的值)。在windows中用户已经登录的情况下则是通过命令set来更改环境变量的。
Java语言是一门跨平台的语言,具有一次编写到处运行的特点。在java的1.0版本中有System.getenv(String key)可以来取得操作系统的环境变量,但由于getenv()具有与操作系统紧密相关的特性,这与java的跨平台的跟本特征相冲突,所以在java1.2中该方法被不推荐使用。而程序员在编程的过程中经常需要用到getenv(),所以在java1.5中getenv()又重新回来了。虽然可以通过getenv()来取得系统的环境变量,但是却没有与getenv()相对应的setenv()函数来改变系统的环境变量。
C语言是一门与平台相关的语言,在多年的发展中积累了数量相当可观的库函数,在stdlib.h函数库中就有getenv(参数省略)与setenv(参数省略)来获取和改变系统的环境变量,这就给我们一个提示:能不能在java语言中调用C语言的函数库?JNI(java本地接口)正给了我们这样一个选择。
1.首先生成ChangeEnv.java文件
/**
* @author sgh
* @version 1.0.0 06/07/21
*/
public class ChangeEnv {
/**
* @param args
*/
static {
try{
System.loadLibrary("change_env");//声明欲加载的动态链接库
}
catch(UnsatisfiedLinkError e ){
System.err.println("Can not load library "+e.toString());
}
}
public native void setEnv(String name ,String value, int replace);//声明一个本地调用接口
}
说明:
1. 动态链接库在windows中是.dll文件,在linux中则是.so文件,但在System.loadLibrary("change_env")中不需要把后缀写出 ,程序会自己判断。
2. 本地接口声明方式为在普通函数前加native关键字
2. 编译java文件 :Javac ChangeEnv.java
3. 使用命令 javah ChangeEnv 生成ChangeEnv.h文件(ChangeEnv.h文件由程序自动生成,程序员不需要作任何改动)
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class ChangeEnv */
#ifndef _Included_ChangeEnv
#define _Included_ChangeEnv
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: ChangeEnv
* Method: setEnv
* Signature: (Ljava/lang/String;Ljava/lang/String;I)V
*/
JNIEXPORT void JNICALL Java_ChangeEnv_setEnv
(JNIEnv *, jobject, jstring, jstring, jint);
#ifdef __cplusplus
}
#endif
#endif
说明:
1.JNIEXPORT void JNICALL Java_ChangeEnv_setEnv
(JNIEnv *, jobject, jstring, jstring, jint)是本地接口函数的声明需要在.cpp文件中实现它
4.编写ChangeEnv.cpp
#include"ChangeEnv.h"
#include<stdio.h>
#include<stdlib.h>
//与ChangeEnv.h中函数声明相同
JNIEXPORT void JNICALL Java_ChangeEnv_setEnv(JNIEnv * env, jobject obj, jstring name, jstring value, jint replace)
{
////从instring字符串取得指向字符串UTF编码的指针
const char * name_char =(const char *) env->GetStringUTFChars(name ,JNI_FALSE);
const char * value_char =(const char *) env->GetStringUTFChars(value ,JNI_FALSE);
//实际调用的C库函数
setenv(name_char,value_char,replace);
//通知虚拟机本地代码不再需要通过name_char访问Java字符串,否则会
//造成内存泄露
env->ReleaseStringUTFChars(name,(const char *)name_char);
env->ReleaseStringUTFChars(value,(const char *)value_char);
return ;
}
5.编译ChangeEnv.cpp文件,生成libchange_env.so文件
gcc -I/home/disk4/sgh/sgh/jrockit/include -I/home/disk4/sgh/sgh/jrockit/include/linux -shared -o libchange_env.so ChangeEnv.cpp
说明:
1. Linux下链接库名称必须以lib开头,否则会无法识别
6. 编写测试程序test.java
import java.io.InputStreamReader;
import java.io.LineNumberReader;
public class test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(System.getenv("PATH"));//打印改变之前的PATH路径
String pathPer = System.getProperty("java.library.path");
pathPer+=":.";
System.setProperty("java.library.path",pathPer);//把当前目录加到动态链接库路径中
ChangeEnv changePath = new ChangeEnv ();//生成一个ChangeEnv对象
changePath.setEnv("PATH","$PATH:/home/disk4/sgh/sgh/soft/slurm34/bin:/home/disk4/sgh/sgh/soft/slurm34/sbin",1);
System.out.println(System.getenv("PATH"));//打印改变之后的PATH路径
}
}
说明:
1. 可以看到PATH路径发生了变化
JNI在windows下的使用
既然所有的.h ,.cpp文件都已写好,我们不防顺便编译以下windows下的动态链接库.dll文件。
这时我们发现在windows下的vc环境中没有setenv(char * name ,char * value ,int replace),而只有putenv(char * key_value)函数,所以我们必须重写ChangeEnv.cpp,为了使ChangeEnv.class对外接口保持一致性,所以我们没有改写ChangeEnv.java中本地函数借口的声明。
1. 重写ChangeEnv.cpp函数
#include"ChangeEnv.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//与ChangeEnv.h中函数声明相同
JNIEXPORT void JNICALL Java_ChangeEnv_setEnv(JNIEnv * env, jobject obj, jstring name, jstring value, jint replace)
{
if(replace==0)//如果replace==0表示不发生置换,直接退出
return ;
////从instring字符串取得指向字符串UTF编码的指针
const char * name_char =(const char *) env->GetStringUTFChars(name ,JNI_FALSE);
const char * value_char =(const char *) env->GetStringUTFChars(value ,JNI_FALSE);
//实际调用的C库函数,把环境变量写成key=value格式
char * key_value=(char *)name_char;
strcat(key_value, "=");
strcat(key_value, value_char);
putenv(key_value);
//通知虚拟机本地代码不再需要通过name_char访问Java字符串,否则会
//造成内存泄露
env->ReleaseStringUTFChars(name,(const char *)name_char);
env->ReleaseStringUTFChars(value,(const char *)value_char);
return ;
}
2. 在vc6中新建一个动态链接库工程,添加ChangeEnv.h和ChangeEnv.cpp到该工程中去
3. 在“工具”----〉“选项-”----〉“目录”中添加java的include路径
C:\Program Files\Java\jdk1.5.0_06\include和C:\Program Files\Java\jdk1.5.0_06\include\win32
4. 单击“运行”,把生成的change_env.dll拷贝到ChangeEnv.java所在的工程目录中
5. 在ChangeEnv.java所在工程中新建一个测试程序test.java
import java.io.InputStreamReader;
import java.io.LineNumberReader;
public class test {
/**
* @param args
*/
public static void main(String[] args) {
ChangeEnv changePath = new ChangeEnv();
changePath.setEnv("PATH", "%PATH%;c:\\", 1);
System.out.println(System.getenv("PATH"));
}
}
JAVA如何调用C函数
能不能换个思路~~~汗啊~!好像用java的函数只能取得lastModified()有一个比较笨的方法
Process p=Runtime.getRuntime().exec("dir startup.bat /t -c");
然后取得p的输出信息就可以得到创建时间了!上面的例子我是得到tomcat的startup.bat这个文件的创建时间,如果你想得到其它文件的创建时间就可以将startup.bat换成其它文件名!study and mark ^_^如果用dir, 取得创建时间的参数是/TC路过学习ing帮您顶java是用native来获取操作系统的支持,从而对文件进行操作。
具体实现类是
java.io.FileSystem
这个类是私有的只能在jdk内部使用,由于这个类没有提供文件时间创建的接口,你也就没有办法用纯java来获得这一个功能(但愿不是这样)。
有一个简单的方法是自己调用系统函数,或者干脆调用各C函数
先写各本地类访问方法
class MyFileSystem{
public native void getCreatFileTime(File f);static {System.loadLibrary(" ");}
public static void main(String[] args) {
new HelloWorld().getCreatFileTime();}}编译本文件然后执行 javah,便会自动产生头文件 MyFileSystem.h
函数的具体实现如下:
#include
#include "MyFileSystem.h"
#include
JNIEXPORT double JNICALL
Java_MyFileSystem_displayHelloWorld(JNIEnv *env, jobject obj){//在这里调用C函数
double time=getFileCreatTime_IN_C(obj)
return time;}高手如云,我心忐忑
奋发图强~高手如云,我心忐忑奋发图强~me too,高手如云,我心忐忑奋发图强~me too,too还有可以复制java.io.FileSystem及相关类的源代码,把所有东西成public的,这样总可以用了吧,
当然把类名也改掉。。。。。顶!
Study!Up!!!顶一下!
支持upFile f = new File("d:\\a.txt");
Date date = new Date();
long L = date.getTime();
f.setLastModified(L);
以上个函数可以设置文件的最后修改时间,以及设置文件为“只读”属性顶!!!
要在java中调用c语言的库,需要使用Java提供了JNI。
举例说明
在c语言中定义一个 void sayHello()函数(打印Hello World);然后在Java中调用这个函数显示Hello Word.
现在分别从Java和C语言两部分说明:
1. Java 部分
首先定义一个HelloNative,在其中申明sayHello函数,函数要申明为Native 类型的.如下:
public class HelloNative {
public native void sayHello();
}
编译这个类,生成class文件:
javac HelloWorld.java
利用javah生成需要的h文件
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: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloNative_sayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
可以看一下上面自动生成的程序,程序include了jni.h,这个头文件在 $JAVA_HOME下的include文件夹下. 还可以发现生成的函数名是在之前的函数名前面加上了Java_HelloNative。
2. C语言部分
根据上面生成的h文件编写相应的代码实现,建立一个 HelloNative.cpp用来实现显示Hello World的函数.如下:
#include <stdio.h>
#include "HelloNative.h"
JNIEXPORT void JNICALL Java_HelloNative_sayHello(JNIEnv *, jobject)
{
printf("Hello World!\n");
}
代码编写完成之后,我们再用gcc编译成库文件,命令如下;
gcc -fPIC -I/usr/lib/jvm/java-7-openjdk-i386/include -I/usr/lib/jvm/java-7-openjdk-i386/include/linux -shared -o libHelloNative.so HelloNative.cpp
这样就会在当前目录下生成一个libHelloNative.so的库文件.这时需要的库已经生成,在C语言下的工作已经完成了.
接下来需要在Java中编写一个程序测试一下.在程序前,需要将我们的库载入进去.载入的方法是调用Java的 System.loadLibrary("HelloNative");
public class TestNative
{
static {
try {
System.loadLibrary("HelloNative");
}
catch(UnsatisfiedLinkError e) {
System.out.println( "Cannot load hello library:\n " + e.toString() );
}
}
public static void main(String[] args) {
HelloNative test = new HelloNative();
test.sayHello();
}
}
但是再编译后,运行的时候,问题又出现了.
Cannot load hello library:
java.lang.UnsatisfiedLinkError: no HelloNative in java.library.path
Exception in thread "main" java.lang.UnsatisfiedLinkError: HelloNative.sayHello()V
at HelloNative.sayHello(Native Method)
at TestNative.main(TestNative.java:13)
载入库失败,但是库明明就是放在当前文件夹下的,怎么会载入失败呢?
用System.getProperty("java.library.path")查看,发现java.library.path中并不u存在当前的目录.主要有以下的几个解决办法:
1) 将生成的库复制到java.library.path有的路径中去,当然这样不是很好
2) 设置环境变量export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ,将当前的目录加入到LD_LIBRARY_PATH中
3) 设置java 的选项,将当前的目录加入到其中 .java -Djava.library.path=. $LD_LIBRARY_PATH
这样之后程序就能够成功的运行了.可以看见显示的"Hello World!"了
关于“java 加载动态链接库怎么使用相对路径”这个话题的介绍,今天小编就给大家分享完了,如果对你有所帮助请保持对本站的关注!
本文来自作者[董明明]投稿,不代表小熊号立场,如若转载,请注明出处:https://www.xx-scm.com/cshi/202604-179294.html
评论列表(4条)
我是小熊号的签约作者“董明明”!
希望本篇文章《java 加载动态链接库怎么使用相对路径》能对你有所帮助!
本站[小熊号]内容主要涵盖:国足,欧洲杯,世界杯,篮球,欧冠,亚冠,英超,足球,综合体育
本文概览:网上科普有关“java 加载动态链接库怎么使用相对路径”话题很是火热,小编也是针对java 加载动态链接库怎么使用相对路径寻找了一些与之相关的一些信息进行分析,如果能碰巧解决你...