在上一篇文章中介绍了Java语言中的native关键字,以及Java语言调用C语言的编译生成本地动态链接库(DLL)实现加法运算的小例子,本文通过一个更加详细的例子,深入讲解Java语言调用C语言的函数。

一. 什么是JNI

  JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境下。

  总的来说,JNI就是一个允许Java语言和其他编程语言(主要是C/C++)通信的接口。C/C++是系统级的编程语言, 可以用来开发任何和系统相关的程序和类库, 但是Java本身编写底层的应用比较难实现, 使用JNI可以调用现有的本地库, 极大地灵活了Java的开发. C/C++的效率是目前最好的语言, 可以使用C/C++来实现一些实时性非常高的部分. C/C++和Java本身都是非常流行的编程语言, 一些大型软件中经常使用语言之间的混合编程.

  一旦使用JNI, JAVA程序就丧失了JAVA平台的两个优点: 程序不在跨平台。要想跨平台,必须在不同的系统环境中重新编译本地语言部分;程序不再是绝对安全的,本地代码的不当使用可能导致整个陈旭崩溃。一个通用的规则是,你应该让本地方法集中在少数几个类当中,这样就降低了Java语言和C/C++之间的耦合性。

  使用JNI实现Java与C语言混合编程的基本步骤如下:

  1. 编写带有native声明的方法的java类
  2. 使用javac命令编译所有的java类
  3. 然后使用javah + 类名生成扩展名为.h的头文件
  4. 使用C/C++实现本地方法
  5. 将C/C++编写的文件生成动态链接库

  下面就完整的介绍一个简单的Java调用C/C++的例子, 这个例子参考了[点击这里跳转原始例子], 并对这个例子做了修改。

二. 编写带有native声明的方法的java类

  首先编写一个文件名为Sample的java程序,代码如下:

 public class Sample
{
public static void main(String[] args)
{
System.loadLibrary("SampleC");
NativeMethodTest nmt = new NativeMethodTest(); int square = nmt.intMethod(5);
boolean bool = nmt.booleanMethod(true);
String text = nmt.stringMethod("java");
int sum = nmt.intArrayMethod(new int[]{1,2,3,4,5,6,7,8,13}); System.out.println("intMethod: " + square);
System.out.println("booleanMethod:" + bool);
System.out.println("stringMethod:" +text);
System.out.println("intArrayMethod:" + sum);
}
} class NativeMethodTest
{
public native int intMethod(int n);
public native boolean booleanMethod(boolean bool);
public native String stringMethod(String text);
public native int intArrayMethod(int[] intArray);
}

  上面NativeMethodTest类中有四个native关键字声明的方法,分别是4种类型的参数, int, boolean, String, int[]. 其中 System.loadLibrary("SampleC"); 这行代码加载了本地动态链接库,在Windows系统下加载的就是SampleC.dll, 在Linux系统下加载的就是SampleC.so。注意不可以在代码中写上扩展名.dll或者.so。本文使用的是Windows系统,加载的是SampleC.dll,但是这个DLL文件就是我们稍后要使用C/C++编译生成的文件。

  这4个native关键字修饰的方法就是需要我们用C/C++实现的方法。

三. 使用javac命令编译所有的java类

  编译Sample.java,使用命令行(Windows下是cmd) javac Sample.java 可以看到生成两个class文件:Sample.class, NativeMethodTest.class

四. 使用javah命令生成.h文件

  在命令行中运行 javah NativeMethodTest 可以生成NativeMethodTest.h文件,注意不是 javah Sample , native关键字修饰的方法在那个类中就使用javah命令生成对应的头文件。NativeMethodTest.h的代码如下:

 /* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class NativeMethodTest */ #ifndef _Included_NativeMethodTest
#define _Included_NativeMethodTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: NativeMethodTest
* Method: intMethod
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_NativeMethodTest_intMethod
(JNIEnv *, jobject, jint); /*
* Class: NativeMethodTest
* Method: booleanMethod
* Signature: (Z)Z
*/
JNIEXPORT jboolean JNICALL Java_NativeMethodTest_booleanMethod
(JNIEnv *, jobject, jboolean); /*
* Class: NativeMethodTest
* Method: stringMethod
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_NativeMethodTest_stringMethod
(JNIEnv *, jobject, jstring); /*
* Class: NativeMethodTest
* Method: intArrayMethod
* Signature: ([I)I
*/
JNIEXPORT jint JNICALL Java_NativeMethodTest_intArrayMethod
(JNIEnv *, jobject, jintArray); #ifdef __cplusplus
}
#endif
#endif

  这个h文件相当于我们在java里面的接口,这里声明了四个方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致。

  在注释中我们可以看到这样一个东西 Signature, 这个是方法的签名. 关于Signature, 下面通过一个表格来说明.

java类型 Signature 备注
boolean Z  
byte B  
char C  
short S  
int I  
long L  
float F  
double D  
void V  
Object L用/分割的完整类名 例如: Ljava/lang/String表示String类型
Array [签名 例如: [I表示int类型的数组, [Ljava/lang/String表示String类型的数组
Method (参数签名)返回类型签名 例如: ([I)I表示参数类型为int数组,返回值int类型的方法

  在每个函数的参数列表中都有JNIEnv *和 jobject两个参数, 这两个参数稍候说明.

五. 使用C/C++实现本地方法

  可以使用C语言来实现, 也可以使用C++来实现, 下面说说C语言的实现.

  创建DLL工程,添加NativeMethodTest.h等头文件,具体方法在这一篇随笔中有详细介绍[点击查看],C程序如下:

 # include "NativeMethodTest.h"
# include <string.h> JNIEXPORT jint JNICALL Java_NativeMethodTest_intMethod
(JNIEnv * env, jobject obj, jint i)
{
return i * i;
} JNIEXPORT jboolean JNICALL Java_NativeMethodTest_booleanMethod
(JNIEnv * env, jobject obj, jboolean b)
{
return !b;
} JNIEXPORT jstring JNICALL Java_NativeMethodTest_stringMethod
(JNIEnv * env, jobject obj, jstring string)
{
const char* str = (*env)->GetStringUTFChars(env, string, );
char cap[];
strcpy(cap, str);
(*env)->ReleaseStringUTFChars(env, string, );
return (*env)->NewStringUTF(env, strupr(cap));
} JNIEXPORT jint JNICALL Java_NativeMethodTest_intArrayMethod
(JNIEnv * env, jobject obj, jintArray array)
{
int i, sum = ;
jsize len = (*env)->GetArrayLength(env, array);
jint * body = (*env)->GetIntArrayElements(env, array, ); for (i=; i<len; i++)
{
sum += body[i];
}
(*env)->ReleaseIntArrayElements(env, array, body, );
return sum;
}

  (*env)->GetStringUTFChars()这个方法, 是用来在Java和C之间转换字符串的, 因为Java本身都使用了双字节的字符, 而C语言本身都是单字节的字符, 所以需要进行转换.

  JNIEnv *是每个函数都有的参数, 它包含了很多有用的方法, 使用起来类似Java的反射, 也提供了这样一个编码转换的函数.

  GetStringUTFChars()和NewStringUTF(), 第一个是从UTF-8转换为C的编码格式, 第二个是根据C的字符串返回一个UTF8字符串.

  ReleaseStringUTFChars()是用来释放对象的, 在Java中有虚拟机进行垃圾回收, 但是在C语言中, 这些对象必须手动回收. 否则可能造成内存泄漏.

  函数的名字一眼看到就可以猜出功能, jni.h中的大部分函数名都是这样.

六. 将C/C++编写的文件生成动态链接库

  将编译生成的SampleC.dll文件拷贝到Sample.class所在的文件夹中,在命令行中运行:java Sample 即可看到如下结果:

intMethod:
booleanMethod:false
stringMethod:JAVA
intArrayMethod:

参考文献:

http://www.cnblogs.com/icejoywoo/archive/2012/02/22/2363709.html

Java语言与C语言混合编程(2)--在Java中调用C语言本地库的更多相关文章

  1. (转)python中调用R语言通过rpy2 进行交互安装配置详解

    python中调用R语言通过rpy2 进行交互安装配置详解(R_USER.R_HOME配置) 2018年11月08日 10:00:11 luqin_ 阅读数:753   python中调用R语言通过r ...

  2. shell中调用R语言并传入参数的两种步骤

    shell中调用R语言并传入参数的两种方法 第一种: Rscript myscript.R R脚本的输出 第二种: R CMD BATCH myscript.R # Check the output ...

  3. 在Windows Phone项目中调用C语言DLL

    在Windows Phone项目中调用C语言写的DLL 最近接到一个需求,需要在WP里调用一个C语言写的DLL,并且说Android和iOS都可以,问我WP是否可以这样? 我说我调研一下,就有了下面的 ...

  4. 一文教你快速学会在matlab的simulink中调用C语言进行仿真

    本文介绍如何在matlab的simulink中嵌入C语言进行多输入多输出的仿真:matlab版本位2015b: 创作不易,如果本文帮到了您: 如果本文帮到了您,请帮忙点个赞

  5. 混合编程:如何用python11调用C++

    摘要:在实际开发过程中,免不了涉及到混合编程,比如,对于python这种脚本语言,性能还是有限的,在一些对性能要求高的情景下面,还是需要使用c/c++来完成. 那怎样做呢?我们能使用pybind11作 ...

  6. 如何在python中调用C语言代码

    1.使用C扩展CPython还为开发者实现了一个有趣的特性,使用Python可以轻松调用C代码 开发者有三种方法可以在自己的Python代码中来调用C编写的函数-ctypes,SWIG,Python/ ...

  7. Python中调用c语言(简单版)

    Python中有时需要调用c程序中的函数.使用ctype库可以很方便地调用c语言.现说明方法,以及注意事项. c程序编译为.so文件: 我们需要的c语言文件为test.c,要从其中调用func(x,y ...

  8. [转]在C#中调用C语言函数(静态调用Native DLL,Windows & Microsoft.Net平台)

    原文:https://blog.csdn.net/yapingxin/article/details/7288325 对于不太了解.Net的人,如果想要了解.Net,我必须给他介绍P/Invoke.P ...

  9. 在Java中调用C/C++本地库

    JNI是Java Native Interface的英文缩写, 中文翻译为本地调用, 自从Java 1.1开始就成为了Java标准的一部分. C/C++是系统级的编程语言, 可以用来开发任何和系统相关 ...

随机推荐

  1. Struts2中的包的作用描述

    asm-3.3.jar作用:操作java字节码的类库包路径及主要类:未提供 asm-commons-3.3.jar作用:提供了基于事件的表现形式包路径及主要类:未提供 asm-tree-3.3.jar ...

  2. IOS-源代码管理工具(SVN)

    一.使用环境 要想利用SVN管理源代码,必须得有2套环境 服务器 用于存储客户端上传的源代码 可以在Windows上安装Visual SVN Server 大部分情况下,公司的开发人员不必亲自搭建SV ...

  3. GlusterFS原创资源

    学习博客: GlusterFS原创资源

  4. C++:创建线程初试

    1.使用CreatThread创建 #include <iostream> #include <Windows.h> using namespace std; /* 创建一个线 ...

  5. C++复习10.对象的初始化拷贝析构函数

    对象的初始化.拷贝构造和析构函数 20131002 构造函数.析构函数.赋值函数是类的基本函数.每一个类只有一个析构函数,但是可以有多个构造函数.多个赋值函数.一般如果类中没有显示的声明和定义上述函数 ...

  6. FMDB给表添加新的字段

    1.首先判断添加的字段是否存在,如果不存在就添加. 2.代码演示: (1)判断是否存在,判断之前先导入头文件确保可以调用FMDB的api(#import “FMDatabaseAdditions.h” ...

  7. L147 Low Cost Study Has High Impact Results For Premature Babies

    No one knows exactly why some babies are born prematurely(早产), but some of the smallest premature ba ...

  8. Laravel 文件夹结构简介

    表 1.1:Laravel 文件夹结构简介 文件夹名称 简介 app 应用程序的业务逻辑代码存放文件夹 app/Console 存放自定义 Artisian 命令文件 app/Http/Control ...

  9. CSS字幕滚动 !!!

    marquee的基本语法:<marquee> … </marquee>,与大多数HTML语法元素一样,它也是成双出现的,被修饰对象就放在起始符和终止符之间.而且它自己可以实现鼠 ...

  10. SQL映射文件

     SQL映射文件的几个顶级元素 mapper - namespace cache - 配置给定命名空间的缓存 cache-ref – 从其他命名空间引用缓存配置 resultMap –用来描述数据库结 ...