首页 > 编程知识 正文

c和java相互调用,c++类内函数互相调用

时间:2023-12-27 22:27:08 阅读:325543 作者:RYAG

本文目录一览:

如何从C中调用Java函数使用JNI

1. 编写并编译J2C.java

import java.lang.management.ManagementFactory;

import java.lang.management.RuntimeMXBean;

public class J2C

{

static

{

try{

// 此处即为本地方法所在链接库名

System.loadLibrary("j2c");

} catch(UnsatisfiedLinkError e)

{

System.err.println( "Cannot load J2C library:n " +

e.toString() );

}

}

//声明的本地方法

public static native int write2proc(int pid);

public static void main(String[] args){

//获取本进程(即主线程)的pid

final RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();

final String info = runtime.getName();

final int index = info.indexOf("@");

if (index != -1) {

final int pid = Integer.parseInt(info.substring(0, index));

System.out.println(info);

System.out.println(pid);

write2proc(pid);

}

try{

Thread.sleep(8000);

} catch(InterruptedException e){

e.printStackTrace();

}

}

}

note:Java程序中System.loadLibrary参数名表示要载入的C/C++共享库,第6步生成的共享库名必须与该参数一致,即System.loadLibrary(Name) 对应共享库名libName.so (共享库名必须以lib开头)

2. 生成C头文件J2C.h:javah J2C

/* DO NOT EDIT THIS FILE - it is machine generated */

#include jni.h

/* Header for class J2C */

#ifndef _Included_J2C

#define _Included_J2C

#ifdef __cplusplus

extern "C" {

#endif

/*

* Class: J2C

* Method: write2proc

* Signature: (I)I

*/

JNIEXPORT jint JNICALL Java_J2C_write2proc

(JNIEnv *, jclass, jint);

#ifdef __cplusplus

}

#endif

#endif

note:1. 头文件自动生成,不要修改它;

2. 函数JNIEXPORT jint JNICALL Java_J2C_write2proc(JNIEnv *, jclass, jint);

按照注释的说明是在J2C.java文件的类J2C的方法write2proc处定义,故C程序的实现函数必须与该处签名一致;

3. 编写C程序J2C.c

#include stdio.h

#include "J2C.h"

JNIEXPORT int JNICALL Java_J2C_write2proc(JNIEnv * env, jobject arg, jint pid)

{

printf("current pid is %dn", pid);

return 0;

}

4. 编译C程序

因为C程序里#include "J2C.h"而J2C.h又#include jni.h, 而gcc里面默认环境并不知道jni.h是什么东西,故编译时需要告诉编译器jni.h的位置( jni.h在jdk 的$JAVA_HOME/include下面),所以才有了上面的编译参数;

因为使用gcc编译得到动态库,在jni调用的时候,某些情况会有异常, 可尝试改用g++。

总结

1. Java中方法的原型声明与C/C++对应的实现文件定义必须一致(可以通过自动生成的C/C++头文件来比较),尤其是类名和方法名;

2. Java中System.loadLibrary()载入的共享库名必须与后面C/C++生成的共享库名一致。

C/C++如何调用JAVA

JAVA 可以调用 native method,可以调用 C 语言在本地编译后的方法。

如果想让 C++ 调用 JAVA 程序,可以用 socket 通信,建立连接后,通过 socket 来传递命令和参数,在 JAVA 程序里根据命令调用不同的方法,并且把调用后的结果通过 socket 传回调用方界面。

在 JAVA 程序里不要用 char(两个字节) 类型传递信息,需要用 byte 类型与 C++ 程序通信。

如果用了 Visual C++ 开发工具,可以上网搜索:winsock,寻找相关资料

如何在C/C++中调用Java

 

环境搭建

为了让本文以下部分的代码能够正常工作,必须建立一个完整的开发环境。首先需要下载并安装JDK 1.3.1,其下载地址为“”。假设安装路径为C:JDK。

下一步就是设置集成开发环境,通过Visual C++ 6的菜单Tools→Options打开选项对话框

将目录C:JDKinclude和C:JDKincludewin32加入到开发环境的Include Files目录中,同时将C:JDKlib目录添加到开发环境的Library Files目录中。这三个目录是JNI定义的一些常量、结构及方法的头文件和库文件。集成开发环境已经设置完毕,同时为了执行程序需要把Java虚拟机所 用到的动态链接库所在的目录C:JDK jreinclassic设置到系统的Path环境变量中。这里需要提出的是,某些开发人员为了方便直接将JRE所用到的DLL文件直接拷贝到系统目录 下。这样做是不行的,将导致初始化Java虚拟机环境失败(返回值-1),原因是Java虚拟机是以相对路径来寻找所用到的库文件和其它一些相关文件的。 至此整个JNI的开发环境设置完毕,为了让此次JNI旅程能够顺利进行,还必须先预备一个Java类。在这个类中将用到Java中几乎所有有代表性的属性 及方法,如静态方法与属性、数组、异常抛出与捕捉等。我们定义的Java程序(Demo.java)如下,本文中所有的代码演示都将基于该Java程序, 代码如下:

package jni.test; /** * 该类是为了演示JNI如何访问各种对象属性等 * @author liudong */ public class Demo { //用于演示如何访问静态的基本类型属性 public static int COUNT = 8; //演示对象型属性 public String msg; PRivate int[] counts; public Demo() { this("缺省构造函数"); } /** * 演示如何访问构造器 */ public Demo(String msg) { System.out.println(":" + msg); this.msg = msg; this.counts = null; } /** * 该方法演示如何访问一个访问以及中文字符的处理 */ public String getMessage() { return msg; } /** * 演示数组对象的访问 */ public int[] getCounts() { return counts; } /** * 演示如何构造一个数组对象 */ public void setCounts(int[] counts) { this.counts = counts; } /** * 演示异常的捕捉 */ public void throwExcp() throws IllegalaccessException { throw new IllegalAccessException("exception occur."); } }

初始化虚拟机

本地代码在调用Java方法之前必须先加载Java虚拟机,而后所有的Java程序都在虚拟机中执行。为了初始化Java虚拟机,JNI提供了一系列的接 口函数Invocation API。通过这些API可以很方便地将虚拟机加载到内存中。创建虚拟机可以用函数 jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args)。对于这个函数有一点需要注重的是,在JDK 1.1中第三个参数总是指向一个结构JDK1_ 1InitArgs, 这个结构无法完全在所有版本的虚拟机中进行无缝移植。在JDK 1.2中已经使用了一个标准的初始化结构JavaVMInitArgs来替代JDK1_1InitArgs。下面我们分别给出两种不同版本的示例代码。

在JDK 1.1初始化虚拟机:

#include int main() { JNIEnv *env; JavaVM *jvm; JDK1_1InitArgs vm_args; jint res; /* IMPORTANT: 版本号设置一定不能漏 */ vm_args.version = 0x00010001; /*获取缺省的虚拟机初始化参数*/ JNI_GetDefaultJavaVMInitArgs(vm_args); /* 添加自定义的类路径 */ sprintf(classpath, "%s%c%s", vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH); vm_args.classpath = classpath; /*设置一些其他的初始化参数*/ /* 创建虚拟机 */ res = JNI_CreateJavaVM(jvm,env,vm_args); if (res 0) { fprintf(stderr, "Can't create Java VM "); exit(1); } /*释放虚拟机资源*/ (*jvm)-DestroyJavaVM(jvm); }

JDK 1.2初始化虚拟机:

/* invoke2.c */ #include int main() { int res; JavaVM *jvm; JNIEnv *env; JavaVMInitArgs vm_args; JavaVMOption options[3]; vm_args.version=JNI_VERSION_1_2;//这个字段必须设置为该值 /*设置初始化参数*/ options[0].optionString = "-Djava.compiler=NONE"; options[1].optionString = "-Djava.class.path=."; options[2].optionString = "-verbose:jni";//用于跟踪运行时的信息 /*版本号设置不能漏*/ vm_args.version = JNI_VERSION_1_2; vm_args.nOptions = 3; vm_args.options = options; vm_args.ignoreUnrecognized = JNI_TRUE; res = JNI_CreateJavaVM(jvm, (void**)env, vm_args); if (res 0) { fprintf(stderr, "Can't create Java VM "); exit(1); } (*jvm)-DestroyJavaVM(jvm); fprintf(stdout, "Java VM destory. "); }

为了保证JNI代码的可移植性,建议使用JDK 1.2的方法来创建虚拟机。JNI_CreateJavaVM函数的第二个参数JNIEnv *env,就是贯穿整个JNI始末的一个参数,因为几乎所有的函数都要求一个参数就是JNIEnv *env。

访问类方法

初始化了Java虚拟机后,就可以开始调用Java的方法。要调用一个Java对象的方法必须经过几个步骤:

1.获取指定对象的类定义(jclass)

有两种途径来获取对象的类定义:第一种是在已知类名的情况下使用FindClass来查找对应的类。但是要注重类名并不同于平时写的Java代码,例如要得到类jni.test.Demo的定义必须调用如下代码:

jclass cls = (*env)-FindClass(env, "jni/test/Demo");//把点号换成斜杠

然后通过对象直接得到其所对应的类定义:

jclass cls = (*env)- GetObjectClass(env, obj); //其中obj是要引用的对象,类型是jobject

2.读取要调用方法的定义(jmethodID)

我们先来看看JNI中获取方法定义的函数:

jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig); jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass class, const char *name, const char *sig);

这两个函数的区别在于GetStaticMethodID是用来获取静态方法的定义,GetMethodID则是获取非静态的方法定义。这两个函数都需要 提供四个参数:env就是初始化虚拟机得到的JNI环境;第二个参数class是对象的类定义,也就是第一步得到的obj;第三个参数是方法名称;最重要 的是第四个参数,这个参数是方法的定义。因为我们知道Java中答应方法的多态,仅仅是通过方法名并没有办法定位到一个具体的方法,因此需要第四个参数来 指定方法的具体定义。但是怎么利用一个字符串来表示方法的具体定义呢?JDK中已经预备好一个反编译工具javap,通过这个工具就可以得到类中每个属 性、方法的定义。下面就来看看jni.test.Demo的定义:

打开命令行窗口并运行 javap -s -p jni.test.Demo 得到运行结果如下:

Compiled from Demo.java public class jni.test.Demo extends java.lang.Object { public static int COUNT; /* I */ public java.lang.String msg; /* Ljava/lang/String; */ private int counts[]; /* [I */ public jni.test.Demo(); /* ()V */ public jni.test.Demo(java.lang.String); /* (Ljava/lang/String;)V */ public java.lang.String getMessage(); /* ()Ljava/lang/String; */ public int getCounts()[]; /* ()[I */ public void setCounts(int[]); /* ([I)V */ public void throwExcp() throws java.lang.IllegalAccessException; /* ()V */ static {}; /* ()V */ }

我们看到类中每个属性和方法下面都有一段注释。注释中不包含空格的内容就是第四个参数要填的内容(关于javap具体参数请查询JDK的使用帮助)。下面这段代码演示如何访问jni.test.Demo的getMessage方法:

/* 假设我们已经有一个jni.test.Demo的实例obj */ jmethodID mid; jclass cls = (*env)- GetObjectClass (env, obj);//获取实例的类定义 mid=(*env)-GetMethodID(env,cls,"getMessage"," ()Ljava/lang/String; "); /*假如mid为0表示获取方法定义失败*/ jstring msg = (*env)- CallObjectMethod(env, obj, mid); /* 假如该方法是静态的方法那只需要将最后一句代码改为以下写法即可: jstring msg = (*env)- CallStaticObjectMethod(env, cls, mid); */

3.调用方法

为了调用对象的某个方法,可以使用函数CallMethod或者CallStaticMethod(访问类的静态方法),根据不同的返回类型而定。这些方 法都是使用可变参数的定义,假如访问某个方法需要参数时,只需要把所有参数按照顺序填写到方法中就可以。在讲到构造函数的访问时,将演示如何访问带参数的 构造函数。

访问类属性

访问类的属性与访问类的方法大体上是一致的,只不过是把方法变成属性而已。

1.获取指定对象的类(jclass)

这一步与访问类方法的第一步完全相同,具体使用参看访问类方法的第一步。

2.读取类属性的定义(jfieldID)

在JNI中是这样定义获取类属性的方法的:

jfieldID (JNICALL *GetFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig); jfieldID (JNICALL *GetStaticFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);

这两个函数中第一个参数为JNI环境;clazz为类的定义;name为属性名称;第四个参数同样是为了表达属性的类型。前面我们使用javap工具获取类的具体定义的时候有这样两行:

public java.lang.String msg; /* Ljava/lang/String; */

其中第二行注释的内容就是第四个参数要填的信息,这跟访问类方法时是相同的。

3.读取和设置属性值

有了属性的定义要访问属性值就很轻易了。有几个方法用来读取和设置类的属性,它们是:GetField、SetField、 GetStaticField、SetStaticField。比如读取Demo类的msg属性就可以用GetObjectField,而访问COUNT 用GetStaticIntField,相关代码如下:

jfieldID field = (*env)-GetFieldID(env,obj,"msg"," Ljava/lang/String;"); jstring msg = (*env)- GetObjectField(env, cls, field);//msg就是对应Demo的msg jfieldID field2 = (*env)-GetStaticFieldID(env,obj,"COUNT","I"); jint count = (*env)-GetStaticIntField(env,cls,field2);

访问构造函数

很多人刚刚接触JNI的时候往往会在这一节碰到问题,查遍了整个jni.h看到这样一个函数NewObject,它应该是可以用来访问类的构造函数。但是 该函数需要提供构造函数的方法定义,其类型是jmethodID。从前面的内容我们知道要获取方法的定义首先要知道方法的名称,但是构造函数的名称怎么来 填写呢?其实访问构造函数与访问一个普通的类方法大体上是一样的,惟一不同的只是方法名称不同及方法调用时不同而已。访问类的构造函数时方法名必须填写 “”。下面的代码演示如何构造一个Demo类的实例:

jclass cls = (*env)-FindClass(env, "jni/test/Demo"); /** 首先通过类的名称获取类的定义,相当于Java中的Class.forName方法 */ if (cls == 0) jmethodID mid = (*env)-GetMethodID(env,cls,"","(Ljava/lang/String;)V "); if(mid == 0) jobject demo = jenv-NewObject(cls,mid,0); /** 访问构造函数必须使用NewObject的函数来调用前面获取的构造函数的定义 上面的代码我们构造了一个Demo的实例并传一个空串null */

数组处理

创建一个新数组

要创建一个数组,我们首先应该知道数组元素的类型及数组长度。JNI定义了一批数组的类型jArray及数组操作的函数NewArray,其中就是数组中元素的类型。例如,要创建一个大小为10并且每个位置值分别为1-10的整数数组,编写代码如下:

int i = 1; jintArray array;//定义数组对象 (*env)- NewIntArray(env, 10); for(; i= 10; i++) (*env)-SetIntArrayRegion(env, array, i-1, 1, i);

访问数组中的数据

访问数组首先应该知道数组的长度及元素的类型。现在我们把创建的数组中的每个元素值打印出来,代码如下:

int i; /* 获取数组对象的元素个数 */ int len = (*env)-GetArrayLength(env, array); /* 获取数组中的所有元素 */ jint* elems = (*env)- GetIntArrayElements(env, array, 0); for(i=0; i len; i++) printf("ELEMENT %d IS %d ", i, elems[i]);

中文处理

中文字符的处理往往是让人比较头疼的事情,非凡是使用Java语言开发的软件,在JNI这个问题更加突出。由于Java中所有的字符都是Unicode编 码,但是在本地方法中,例如用VC编写的程序,假如没有非凡的定义一般都没有使用Unicode的编码方式。为了让本地方法能够访问Java中定义的中文 字符及Java访问本地方法产生的中文字符串,我定义了两个方法用来做相互转换。

· 方法一,将Java中文字符串转为本地字符串

/** 第一个参数是虚拟机的环境指针第二个参数为待转换的Java字符串定义第三个参数是本地存储转换后字符串的内存块第三个参数是内存块的大小 */ int JStringToChar(JNIEnv *env, jstring str, LPTSTR desc, int desc_len) { int len = 0; if(desc==NULLstr==NULL) return -1; //在VC中wchar_t是用来存储宽字节字符(UNICODE)的数据类型 wchar_t *w_buffer = new wchar_t[1024]; ZeroMemory(w_buffer,1024*sizeof(wchar_t)); //使用GetStringChars而不是GetStringUTFChars wcscpy(w_buffer,env-GetStringChars(str,0)); env-ReleaseStringChars(str,w_buffer); ZeroMemory(desc,desc_len); //调用字符编码转换函数(Win32 API)将UNICODE转为ASCII编码格式字符串 //关于函数WideCharToMultiByte的使用请参考MSDN len = WideCharToMultiByte(CP_ACP,0,w_buffer,1024,desc,desc_len,NULL,NULL); //len = wcslen(w_buffer); if(len0 len

当然这只是其中之一方法:还有其它的方法欲了解参考资料有很多

JAVA如何调用C函数

要在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怎么引用c

Java调用C语言程序时,主要是涉及到操作系统底层的事件。这种时间Java无法处理,例如用户上传一个视频文件,需要后台给视频加上水印,或者后台分离视频流和音频流。只能通过调用C语言处理。

使用Java如何去调用C语言的接口呢?使用Java的JNI技术。

具体调用步骤如下:

1.首先创建Java文件 HelloJni.java ,并创建native方法。

2.编译Java文件并生成java头文件。

3.创建C语言文件,HelloWorld.c。

4.生成动态链接库文件 libhello.so。

5.设置动态链接库文件的目录。

6.把刚才生成的so文件拷贝到/home/lib下,然后执行class文件。

如何用C语言调用JAVA的类文件

Java可以通过JNI调用本地C语言方法,而本地C语言方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式),通过调用本地的库文件的内部方法,使Java可以实现和本地机器的紧密联系,调用系统级的各接口方法。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。