c 通过jni调用java_使用c通过jni调用java

编译环境:

fedora16

gcc (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2)

java version "1.6.0_31"

Java(TM) SE Runtime Environment (build 1.6.0_31-b04)

Java HotSpot(TM) Server VM (build 20.6-b01, mixed mode)

准备工作:

首先需要安装jdk和gcc(或者其他c编译器也可以)并配置相应的环境变量(可自行在网上搜索,这个资料很多的),配好之后在命令行运行gcc --version和java -version出现版本号就代表安装配置成功了:

[roysong@roysong c]$ gcc --version

gcc (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2)

Copyright © 2011 Free Software Foundation, Inc.

本程序是自由软件;请参看源代码的版权声明。本软件没有任何担保;

包括没有适销性和某一专用目的下的适用性担保。

[roysong@roysong c]$ java -version

java version "1.6.0_31"

Java(TM) SE Runtime Environment (build 1.6.0_31-b04)

Java HotSpot(TM) Server VM (build 20.6-b01, mixed mode)

然后需要将jdk中的虚拟机库配置到环境路径中,我是在/etc/ld.so.conf.d/下面新建了一个jni-i386.conf文件,内容如下:

/usr/java/jdk1.6.0_31/jre/lib/i386/client

然后运行一下ldconfig –v,让配置文件起作用,也可以直接编辑/etc/ld.so.conf文件,将这个路径加入进去.这个路径的含义就是$JAVA_HOME/jre/lib/i386/client,其中

$JAVA_HOME换成你自己的jdk安装路径就可以了.这是为了找到libjvm.so.因为我是32位的机器,所以路径中是i386,

我估计64位的机器这个目录是不一样的,根据libjvm.so文件的路径自行替换即可.

程序编写和编译:

程序就分成了c的部分和java的部分,首先我们先看看java的部分,我是把在同一目录下新建了两个文件夹分别叫c和java,

作为c程序的路径和java的classpath,然后在java文件夹下面新建包com.test.base64,java程序的目录结构即:

java/com/test/base64.接着,我们在base64目录下面新建一个Hello.java:

package com.test.base64;

public class Hello{

public String hello (String name){

return "hello,"+name;

}

}

结构非常简单,不过有参数有返回值就可以代表大部分情况了.然后我们使用javac对这个文件进行编译以产生一个class

文件:

[roysong@roysong java]$ cd com/test/base64/

[roysong@roysong base64]$ javac Hello.java

[roysong@roysong base64]$ ls

Hello.class Hello.java

编译成功后,在base64目录下会出现Hello.class这个文件.

然后我们回到c文件夹,新建一个c源文件,我的是cfjIns.c,开始编写c的程序:

#include

#include

/**

*初始化jvm

*/

JNIEnv* create_vm() {

JavaVM* jvm;

JNIEnv* env;

JavaVMInitArgs args;

JavaVMOption options[1];

args.version = JNI_VERSION_1_6;

args.nOptions = 1;

options[0].optionString = "-Djava.class.path=../java";

args.options = options;

args.ignoreUnrecognized = JNI_FALSE;

JNI_CreateJavaVM(&jvm, (void **)&env, &args);

return env;

}

这是cfjIns.c的第一部分,头文件声明

#include

非常重要,这是引用的$JAVA_HOME/include/jni.h,一会儿我们在编译时会加上这个路径.

其次就是

args.version = JNI_VERSION_1_6;

这是jni的版本信息,需要跟你自己的jdk中jni版本对应,jdk1.6和jdk1.7的jni版本都是上面这个.

options[0].optionString = "-Djava.class.path=../java";

这个参数是指明你自己java程序的类路径(classpath),因为我的c文件夹和java文件夹在同一目录下,所以我用了一个

相对路径来指明classpath.

cfjIns.c中获取类定义的函数:

/**

* 根据全限定类名来获取类的定义

*/

jclass create_class(JNIEnv* env,char *className){

jclass cls = (*env)->FindClass(env,className);

if(cls == 0){

printf("class-[%s] find error\n",className);

return;

}

return cls;

}

这个函数有两个参数,第一个就是我们上面通过create_vm函数创建的jvm环境,第二个是全限定的类名字符串,

比如:"com/test/base64/Hello",返回值即对应的类.

cfjIns.c中获取类实例的函数:

/**

*通过无参构造函数来获取对应类的实例

*/

jobject getInstance(JNIEnv* env, jclass obj_class)

{

jmethodID construction_id = (*env)->GetMethodID(env,obj_class, "", "()V");

jobject obj = (*env)->NewObject(env,obj_class, construction_id);

if(obj == 0){

printf("java class instance failed\n");

return;

}

return obj;

}

这个函数的第一个参数是jvm环境,第二个是通过上面的create_class函数获取的类定义.

cfjIns.c中获取方法定义的函数:

/**

* 根据类\方法名\返回值\参数获取类中对应的方法定义

*/

jmethodID get_method(JNIEnv* env,jclass cls,char *methodName,char *key){

jmethodID mid = (*env)->GetMethodID(env,cls,methodName,key);

if(mid == 0){

printf("method-%s is not found\n",methodName);

return;

}

return mid;

}

这儿我们需要注意的是最后一个参数字符串key,这个是方法签名,用于指明方法的参数和返回值类型,格式是

"(参数类型声明)返回值类型声明".类型声明的值参照下面的对应表:

Java类型

对应的签名

boolean

Z

byte

B

char

C

shrot

S

int

I

long

L

float

F

double

D

void

V

Object

L用/分割包的完整类名;  Ljava/lang/String;

Array

[签名       [I       [Ljava/lang/String;

举个例子,如果方法的参数是int,返回值是void,那么方法的签名就是"(I)V";如果方法的参数是String,返回值

也是String,则方法的签名就是"(Ljava/lang/String;)Ljava/lang/String;",注意,如果是引用类型,类名后面必须

带有一个分号.

我们也可以通过javap来查看类中方法参数和返回值的签名:

[roysong@roysong c]$ cd ../java/com/test/base64/

[roysong@roysong base64]$ javap -s -private Hello

Compiled from "Hello.java"

public class com.test.base64.Hello extends java.lang.Object{

public com.test.base64.Hello();

Signature: ()V //构造函数的签名

public java.lang.String hello(java.lang.String);

Signature: (Ljava/lang/String;)Ljava/lang/String; //hello方法的签名

}

然后是一个将c语言中的字符串转换为java中String的工具函数:

/**

* 转换c中的字符串为java.lang.String,这个方法是从网上找到的,感谢原作者天末凉风

*/

jstring stoJstring(JNIEnv* env, const char* pat)

{

jclass strClass = (*env)->FindClass(env,"Ljava/lang/String;");

jmethodID ctorID = (*env)->GetMethodID(env,strClass, "", "([BLjava/lang/String;)V");

jbyteArray bytes = (*env)->NewByteArray(env,strlen(pat));

(*env)->SetByteArrayRegion(env,bytes, 0, strlen(pat), (jbyte*)pat);

jstring encoding = (*env)->NewStringUTF(env,"utf-8");

return (jstring)(*env)->NewObject(env,strClass, ctorID, bytes, encoding);

}

有了上面的例子,这个函数就很好理解了,首先获取到java.lang.String的类定义,然后获取构造函数,然后将c中的字符串

转化为字节流并设置编码格式,最后产生一个新的java.lang.String对象并返回.

这下我们就准备齐全了,开始编写调用函数:

void invoke(){

JNIEnv* env = create_vm(); //初始化java虚拟机

jclass cls = create_class(env,"com/test/base64/Hello");//根据类名找到对应的类

jobject obj = getInstance(env,cls);//然后根据类获取对应的实例

jmethodID hello = get_method(env,cls,"hello","(Ljava/lang/String;)Ljava/lang/String;");//根据类\方法名和签名获取到对应的方法

jstring name_str = (*env)->CallObjectMethod(env,obj,hello,stoJstring(env,"a"));//传入参数调用方法

const char* pname = (*env)->GetStringUTFChars(env,name_str, NULL);//将返回的java字符串转换为c字符串

printf("the result is:%s\n",pname);//打印出调用的结果

}

调用函数编写完成后,用一个main函数来运行一下查看结果:

int main(int argc, char **argv) {

invoke();

}

至此,c的源代码cfjIns.c就编写完成了,我们开始编译cfjIns.c:

[roysong@roysong c]$ gcc -o cfj cfj.c -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -L$JAVA_HOME/jre/lib/i386/client -ljvm

[roysong@roysong c]$ ls

cfjIns cfjIns.c

编译命令中我们使用-I选项加入了两个头文件路径,$JAVA_HOME/include这个是为了引用到jni.h,而

$JAVA_HOME/include/linux这个为了引用到jni_md.h,因为jni.h中有对jni_md.h的引用.我的操作系统是fedora,所以

jni_md.h在$JAVA_HOME/include的linux文件夹下面,其他操作系统可能路径不同,找到jni_md.h文件的路径进行替换

即可.使用-L选项是为了引用到java虚拟机的库文件libjvm.so,-l选项(注意,这是小写的L,而不是大写的i)是声明具体引用

的库jvm.如果libjvm.so文件的路径与我的不同,找到libjvm.so文件的路径进行替换即可.

编译成功后,目录下面会出现一个可运行的cfj文件,如果一切顺利,我们运行它就可以得到预期的结果:

[roysong@roysong c]$ ./cfjIns

hello,a

显示正常,调用完成

75a3bf8974c9bd5a56863a745113d139.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/560513.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言文件与数组之间输入输出操作

C语言文件与数组之间输入输出操作 文件存到数组里面&#xff1a; #include<iostream> #include<fstream> #include<string> #include<cstdio> #include<cstdlib> using namespace std; int main() { fstream fs("test.txt");istream…

java调用scilab_Java调用Scilab-编译运行Javasci v2

1 Scilab简要介绍Scilab 是由法国国家信息、自动化研究院(INRIA)的科学家们开发的“开放源码”软件。Scilab是开源的软件&#xff0c;用户不仅可以在Scilab的许可证条件下自由使用该软件&#xff0c;还可以根据自己需要修改源代码&#xff0c;使之更加符合自身需要。与Matlab类…

java8 foreach 异常_错误处理 – 在java 8流foreach中抛出异常

我正在使用java 8流,我不能在流的foreach中抛出异常.stream.forEach(m -> {try {if (isInitial) {isInitial false;String outputName new SimpleDateFormat(Constants.HMDBConstants.HMDB_SDF_FILE_NAME).format(new Date());if (location.endsWith(Constants.LOCATION_S…

java integer reverse_Leetcode7 Reverse Integer Java实现及分析

首先public int reverse(intx) {int ans 0;while(x!0) {int temp x%10;x/10;ans ans*10temp;}returnans;}但是这样无法检验最终结果是否溢出。我们可以看到&#xff0c;溢出的原因是ans*10temp>MAX或ans*10temp对于正数&#xff0c;举个小点的例子&#xff0c;一个6bit的…

java swing linux_Linux下关于解决JavaSwing中文乱码的情况

前两天在linux中运行java 老是出乱码&#xff0c;很苦恼&#xff0c;后来网上找了好多解决办法。有些可行&#xff0c;有些不可行&#xff0c;今天总结一下。redhed 貌似没出现乱码 本身就jdk就支持中文红旗linux suse等都不支持&#xff0c;需要自己手工配置&#xff0c;解决…

单片机实验:外部中断按键

这是之前写的&#xff0c;一直没发。 实验内容&#xff1a; 实验大致上就是说&#xff1a;按键不按的时候&#xff0c;灯一直亮&#xff0c;按键按下的时候&#xff0c;灯不亮&#xff0c;报警器响。现在给了汇编语言&#xff0c;要改成c语言&#xff0c;并在proteus里面仿真…

java 重复代码优化_Java代码优化

优化实践1.try{}catch(){}使用1)查询中不要使用try{}catch(){}语句2)循环中不要使用try{}catch(){}语句3)异常处理不要对于所有的异常捕获都使用Exception&#xff0c;最好是捕获对应的异常类型&#xff0c;处理日志方式&#xff1a;1、打印错误日志2、调用错误业务逻辑处理函数…

java 06_JAVA06 数组

1.设计思路&#xff1a;首先设置长度为10的数组&#xff0c;设置Random引用对象&#xff0c;在循环中生成一个随机数存储在数组中&#xff0c;累加和&#xff0c;利用对话框输出结果。2.程序流程图&#xff1a;3.源程序代码&#xff1a;1 //2016/11/062 //随机生成10个数&#…

java service 事物_Service 事务(JdbcUtils 升级)

1. DAO 事务// 在 DAO 中处理事务真是"小菜一碟"public void xxx(){Connection con null;try{con JdbcUtils.getConnection();con.setAutoCommit(false); // 开启事务QueryRunner qr new QueryRunner();String sql ...;Object[] params ...;qr.update(con,sql,p…

java 8.0 sinffer_jpcap 配置方法,问题解决,模拟sniffer程序。(附JAVA程序,jar,dll包等环境)...

一、 Eclipse环境下安装与配置Jpcap相关源程序、jpcap jar包&#xff0c;dll包&#xff0c;帮助文档。1、下载安装winpcap2、jpcap官方文件中lib包下&#xff0c;有两个文件&#xff0c;jpcap.dll和jpcap.jar。jpcap.dll-->JAVA安装路径/jre1.6.0_06/bin (JRE目录)jpcap.j…

mysql工作中遇到的问题_MySQL工作中遇到的问题记录

1&#xff1a;log_slave_updates:从库1搭建级联从库2&#xff0c;从库1需要开启log_slave_updates&#xff0c;修改/etc/my.cnf&#xff0c;增加一行log_slave_updates1&#xff0c;重启数据库。http://blog.itpub.net/12679300/viewspace-1319263/2&#xff1a;ERROR 1418 (HY…

java 基本格式

java基本格式&#xff1a; 所有的java程序必须放在一个类之中才可以执行。 主方法main是整个java程序的入口&#xff0c;所有程序都是从public static void main(String[] args)开始运行的。 类定义有两种形式&#xff1a; public class:文件名和类名一致。每一个*.java文件…

python xgboost实战_史上最详细的XGBoost实战

0. 环境介绍Python 版 本&#xff1a; 3.6.2操作系统  &#xff1a; Windows集成开发环境&#xff1a; PyCharm1. 安装Python环境安装Python首先&#xff0c;我们需要安装Python环境。本人选择的是64位版本的Python 3.6.2。去Python官网https://www.python.org/选择相应的版本…

java web导入tomcat_记一次在服务器上导入javaweb 项目的经历---tomcat服务器-Go语言中文社区...

1. 导入数据库远程linux服务器mysql数据库导入和导出.sql文件大部分情况本地开发环境为windows&#xff0c;部署的服务器为Linux&#xff0c;本地数据库导出.sql文件后需要远程导入服务器&#xff0c;具体如下。首先连接服务器&#xff0c;即服务器ip&#xff0c;协议&#xff…

java 2d绘图 stroke_Java标准教程:Java 2D绘图--第4章使用Text

Java标准教程&#xff1a;Java 2D绘图--第4章使用Text本节介绍文本API的用法&#xff0c;以及他们的渲染能力。至今为止&#xff0c;您已经有了基本的Java 2D文本API&#xff0c;同时知道如何设置字体和位置&#xff0c;以及绘制文本。本节扩展了这些知识&#xff0c;同时更深入…

java 定义和导入包

java定义和导入包&#xff1a; 为了更好地组织类&#xff0c;java提供了包机制。把功能相似或相关的类或接口组织在同一个包中&#xff0c;方便类的查找和使用。同一个包中的类名不同&#xff0c;不同包中类名可以相同。同时调用两个不同包中相同类名的类时&#xff0c;应该加…

php李捷,【问题解答】蝶泳手外划的作用

一直以来总是弄不明白&#xff0c;蝶泳手外划的作用&#xff0c;以及正确的外划动作&#xff0c;请指教。”我&#xff1a;先上两个图&#xff0c;您自己分析一下&#xff1a;我&#xff1a;蝶泳的划手路线和打腿的幅度和力度是相匹配的&#xff0c;如果打腿幅度小频率快&#…

ltrim函数php,php ltrim函数怎么用?

php ltrim()函数用于删除字符串左边的空格或其他预定义字符&#xff0c;语法为“ltrim(string,charlist)”&#xff0c;参数string指定需要处理的字符串&#xff0c;参数charlist指定要从字符串中删除哪些字符&#xff1b;然后返回已修改的字符串。php ltrim函数作用&#xff1…

java strcpy,详解C语言中strcpy()函数与strncpy()函数的使用

C语言strcpy()函数&#xff1a;复制字符串头文件&#xff1a;#include 定义函数&#xff1a;char *strcpy(char *dest, const char *src);函数说明&#xff1a;strcpy()会将参数src 字符串拷贝至参数dest 所指的地址。返回值&#xff1a;返回参数dest 的字符串起始地址。附加说…

使用github+hexo搭建静态blog

解决了个人博客图片不显示问题。解决了打开页面产生404问题。学习了如何更换界面主题。学习了如何使用Git。 注&#xff1a;这篇文章仅仅是入个门&#xff0c;了解一下怎么使用githubhexo创个静态blog&#xff0c;具体blog细节比如个人介绍&#xff0c;评论插件&#xff0c;音…