JAVA中调用C语言函数的实现方式

 更新时间:2023年08月02日 09:14:39   作者:猪哥-嵌入式  
这篇文章主要介绍了JAVA中调用C语言函数的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

背景知识

本地代码

在JAVA中使用其他语言的代码(如C/C++)称为本地代码。

历史原因

JAVA的早期阶段,很多人认为使用C和C++来加速JAVA应用中的关键部分是个好主意,但是实际上,虽然JAVA的代码确实没有纯C的运行快,但是JAVA平台实现要更快,也更稳定。

本地代码的应用场景

本地代码,比如C,对于跨平台需求,需要针对支持的平台提供单独的本地类库,而且使用C/C++编写的代码没有对通过使用无效指针所造成的内存腹泻提供任何包含,比如内存回收等,所以会容易破坏程序。

因此,只有在必要的时候才使用本地代码,如下三种场景:

  • 1.应用需要访问的系统特性和设备通过JAVA平台无法实现。
  • 2.已经有了大量的测试过和调试过的用另一种语言编写的代码,比如图像算法,并且知道如何将其导出到所有的目标平台上。
  • 3.通过基准测试,编写的JAVA代码比其他语言编写的等价代码要慢。

JNI

JNI是Java Native Interface的缩写,JNI是JAVA平台专门用于和本地C代码进行相互操作的API,称为JAVA本地接口。

JNI开发流程

  • 1.在JAVA中先声明一个native方法。
  • 2.通过javac -h或javah -jni命令导出JNI使用的C头头文件。
  • 3.使用C实现本地方法。
  • 4.将本地代码变异成动态库,windows下是.dll文件,linux下是.so文件。
  • 5.在JAVA程序中加载步骤4中生成的类库,执行JAVA程序,最终实现JAVA本地代码。

JNI头文件规则

在JAVA中,调用本地代码,需要实现本地代码,需要编写一个相应的C函数,而C函数需要按照JAVA虚拟机的规则来实现,其规则如下:

  • 1.使用完成的JAVA方法名,比如上例中,HelloNative.greeting,如果该类属于某个包,还需要在前面添加包名称,比如com.horstmann.HelloNative.greeting.
  • 2.用下划线’_‘替换掉1中的所有’.’,然后添加上Java_前缀,比如:Java_HelloNative_greeting或Java_com_horstmann_HelloNative_greeting
  • 3.如果类名中含有非ASCII字母或数字,如’_’,’$‘或大于’\u007F’的Unicode字符,用_0xxxx来替代,xxxx是该字符的Unicode值的4个十六进制数序列。

示例

本例中,通过简单的打印功能C函数来举例,在C中使用printf来实现某个打印函数,在JAVA中调用该功能函数。

1. 使用native创建一个本地方法

JAVA中使用关键字native表示本地方法,在JAVA类中声明一个方法,我们先创建一个HelloNative类,

代码如下:

public class HelloNative {
    public static native void greeting();
}

2. 使用javac -h生成头文件

使用 -h 标志运行javac (java 编译工具),提供头文件存放目录,实现对应头文件的生成。

命令代码如下:

javac -h ./ HelloNative.java

运行上述命令后,就会在./ (当前目录)下自动生成一个名为 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:    greeting
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloNative_greeting
  (JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif

其中JNIEXPORT和JNICALL为宏定义,在头文件jni.h中定义,jni.h在JDK安装包中已经包含。

他们的作用是为自动装在库的导出函数表明了依赖于编译器的说明符。

3. 编写本地方法的C代码

我们需要根据头文件中本地方法的声明原型,使用C实现,

程序如下:

#include <stdio.h>
#include "HelloNative.h"
JNIEXPORT void JNICALL Java_HelloNative_greeting(JNIEnv *env, jclass cl)
{
    printf("this is hello native .c printf\n");
}

上述代码中,include了步骤2生成的头文件,另外补充了JNIEnv和jclass,默认的参数。

4. 在linux下,编写该C代码的动态库

需要说明的是,由于该C代码引用了jdk中的 jni.h头文件,所以生成动态库时,需要引用JDK的头文件位置,

命令码如下:

gcc -fPIC -I/opt/ctools/jdk1.8.0_301/include/ -I/opt/ctools/jdk1.8.0_301/include/linux/ -shared -o libHelloNative.so HelloNative.c

其中/opt/ctools/jdk1.8.0_301/include/ 和 /opt/ctools/jdk1.8.0_301/include/linux为依赖jdk的头文件目录。

编译生成了 libHelloNative.so 共享库。

5. 在JAVA程序中加载步骤5中生成的类库,执行

在JAVA程序中,通过System.loadLibrary方法调用动态库,我们再编写一个HelloNativeTest测试类,

代码如下:

public class HelloNativeTest {
	public static void main(String[] args)
	{
		HelloNative.greeting();
	}
	static
	{
		System.loadLibrary("HelloNative");
	}
}

使用javac 编译该测试类

javac HelloNativeTest.java

将步骤4中生成的动态库添加到库路径中,命令如下:

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

编译生成HelloNativeTest.class 字节码, 使用java命令执行

java HelloNativeTest

执行命令结果如下:

this is hello native .c printf

至此就实现了在JAVA中调用C本地代码的完整示例。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • java中注解的实现原理详解

    java中注解的实现原理详解

    这篇文章主要介绍了java中注解的实现原理详解,注解也叫元数据,例如我们常见的@Override和@Deprecated,注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解,需要的朋友可以参考下
    2023-10-10
  • Java-lambda表达式入门看这一篇就够了

    Java-lambda表达式入门看这一篇就够了

    lambda表达式最简单的作用就是用于简化创建匿名内部类对象,Lambda表达式是一个可传递的代码块,可以在以后执行一次或多次,下面通过本文给大家介绍Java-lambda表达式入门教程,感兴趣的朋友一起看看吧
    2021-05-05
  • 详解Eclipse 字体、字号的设置、最佳字体推荐

    详解Eclipse 字体、字号的设置、最佳字体推荐

    这篇文章主要介绍了Eclipse 字体、字号的设置、最佳字体推荐,需要的朋友可以参考下
    2020-09-09
  • Java实现猜数程序

    Java实现猜数程序

    这篇文章主要为大家详细介绍了Java实现猜数程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-12-12
  • 浅谈关于spring profile的误解

    浅谈关于spring profile的误解

    这篇文章主要介绍了浅谈关于spring profile的误解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • Spring MVC 请求映射路径的配置实现前后端交互

    Spring MVC 请求映射路径的配置实现前后端交互

    在Spring MVC中,请求映射路径是指与特定的请求处理方法关联的URL路径,这篇文章主要介绍了Spring MVC 请求映射路径的配置,实现前后端交互,需要的朋友可以参考下
    2023-09-09
  • java和 javaw 及 javaws的区别解析

    java和 javaw 及 javaws的区别解析

    这篇文章主要介绍了java和 javaw 及 javaws的区别解析,本文通过实例给大家详细介绍,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-06-06
  • Java使用递归法解决汉诺塔问题的代码示例

    Java使用递归法解决汉诺塔问题的代码示例

    这篇文章主要介绍了Java使用递归法解决汉诺塔问题的代码示例,汉诺塔问题是使用递归解决问题的经典范例,用到的算法非常简单,需要的朋友可以参考下
    2016-04-04
  • Java事件处理机制(自定义事件)实例详解

    Java事件处理机制(自定义事件)实例详解

    这篇文章主要介绍了Java事件处理机制(自定义事件)实例详解的相关资料,需要的朋友可以参考下
    2016-12-12
  • spring boot整合CAS配置详解

    spring boot整合CAS配置详解

    这篇文章主要介绍了spring boot整合CAS配置详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05

最新评论