jni——java跨语言利器

        作为一个非专业的专业开发者,笨(不系统)是天性,积累是习惯,一个小小的机箱内的东西对个人来说,像宇宙一样不简单。痛苦也好,辛苦也罢,在这个局促的机箱中遨游吧。扯远了哈,个人觉得JNI是java语言与系统交互的基础,如I/O处理,对象创建等都离不开JNI的实现。因此,笔者决定以此作为学习JDK的初篇,慢慢去揭开java语言的面纱,没准哪天笔者自己写一个Eava或者Hava也说不定,期待吧,比卡丘!

一、基本介绍

        JNI是Java Native Interface的缩写,即java本地接口(来源:度娘)。

        我们知道Windows系统也好(本文章下文做的操作皆以此系统作为运行环境),Linux系统也罢,汇编语言只在系统的开发中占一小部分,主要应用在与机器交互的内核命令中,其中一大部分还是用c/c++开发的,特别是系统提供给开发者和用户的库函数中。因此,一些我们比较熟悉的跨平台语言如python、java、golang等(这个排序不代表笔者心中的地位,笔者一直认为机器语言是最好的语言,一切尽在0|1中)底层都是用c/c++开发的。

        或者我们也可以这样理解,高级语言只是一种表达方式,比如一块石头,你说“stone”还是“石头”,最终想要给别人展示的都是那块玩意儿,我们只不过是用不同的语言体系赋予了一个物什抽象表达,而JNI则是java语言体系对于本地函数(库函数、自定义c/c++函数)的转换方式罢了。

二、静态链接和动态链接

        一个c语言项目需要经过预处理、编译、汇编、链接阶段才能生成可执行文件,经过汇编之后就生成了包括我们机器认识的机器指令的对象文件。对象文件不是可直接执行的文件,特别是,当我们在一个对象文件中还调用了别的文件的函数时,就需要通过链接的方式,让两个文件生成调用与被调用关系。在c语言中链接的方式有两种,即静态链接和动态链接。

2.1 静态链接

        静态链接很像是结了婚,别小看那个小本本,它在让两个人都有了家的存在的同时,定义了一个法定事实,即你的是我的,我的还是我的,在这个过程中调用和被调用函数都被放进了可执行文件中,因此,可执行文件脱离了静态库也是可以运行的。我们来看一个示例:

add.c

int add(int a,int b)
{
	return a + b;
}

multi.c

int multi(int a,int b)
{
	return a * b;
}

calc.h

int add(int a, int b);
int multi(int a,int b);

步骤如下:

        1)新建文件夹 lib,在文件夹中创建并编辑上面三个文件

        2)将.c文件生成静态链接库需要借助编译器,可以下载安装mingW或其他的编译器,作者是用的mingW,下载安装的过程作者就略述了。

        3)在lib文件夹下打开DOS,生成静态链接库

#将上面编辑的.c文件在命令行中编译成.o文件(对象文件)

gcc -c *.c

#生成静态链接库

ar rcs wife.a *.o

        此时,我的静态链接库就生成了,可以通过命令 ar -t wife.a 查看到库中包含了 add.o 和  multi.o 两个文件。这时候我们重新创建

        4)新创建一个文件夹home,把 calc.h 和 wife.a 文件都拿过来,创建并编辑 main.c 文件

#include <stdio.h>
#include <stdlib.h>

#include "calc.h"

int main()
{
    int a = 2;
    int b = 5;
    int c = add(a, b);
	int d = multi(a, b);
    printf("the result of %d + %d is %d\n", a, b, c);
    printf("the result of %d * %d is %d\n", a, b, d);
    return 0;
}

        5)在 home 文件夹下运行DOS并执行下面的命令,就能生成可执行文件main.exe,这个main.exe文件是可以脱离wife.a直接执行的。

gcc main.c wife.a -o main

2.2 动态链接

        动态链接像情人关系多一点,链接库是独立存在的,并不会被包含在可执行文件中,而且,它不一定只是一个调用者的情人,也可以同时是很多调用者的情人。通过动态链接生成的可执行文件,不可脱离链接库运行,关系不牢靠。

        生成动态链接只需要对上面生成静态链接的命令做一下修改就行:

gcc -shared -o lover.dll *.o

        创建hotel文件夹,把main.c、calc.h 和 lover.dll 都放在该文件夹下,在 hotel文件夹下运行DOS并执行下面的命令,就能生成可执行文件main.exe,如果我们此时把lover.dll,发现main.exe是无法运行的。

gcc main.c lover.dll -o main

三、java和动态链接

        通过第二节的介绍,我们对动态链接应该也有了一定的了解,java中的native方法正是通过JNI调用动态链接库实现对自定义c函数的调用的。话不多说,直接上步骤吧:

        1)重新创建一个jni文件夹在里面编辑我们本地方法的java类


/**
 * @auther Elean
 * @date 22/7/2024 下午11:29
 * @description
 */
public class Demo {
    public native String myMachine();
}

        2)在文件夹下打开DOS,通过命令行生成对应.c文件的头文件

javac -h .\ Demo.java

        打开头文件,可以看到其内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Demo */

#ifndef _Included_Demo
#define _Included_Demo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Demo
 * Method:    myMachine
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_Demo_myMachine
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

注:关键字解释

        JNIEXPORT:在JNI中定义的宏,相当于java中的访问修饰符,可以不关注 

        JNICALL:与JNIEXPORT类似,也是在JNI中定义的宏,但目前无实际意义

        jstring:与java中的String相对应,是在jni中定义的一种引用类型,类型对应关系感兴趣的话可以自行查资料。

  定义的宏:

#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility)
  #define JNIEXPORT     __attribute__((visibility("default")))
  #define JNIIMPORT     __attribute__((visibility("default")))
#else
  #define JNIEXPORT
  #define JNIIMPORT
#endif

  #define JNICALL

        3)根据头文件,编写我们自己的.cpp或.c文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Demo */

#ifndef _Included_Demo
#define _Included_Demo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Demo
 * Method:    myMachine
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_Demo_myMachine(JNIEnv *env, jobject obj)
{
    char* str = "this is elean's machine";
    return env->NewStringUTF(str);
}

#ifdef __cplusplus
}
#endif
#endif

        4)生成动态链接库

                -I:大写i,生成动态链接时需要引入JDK中include下的jni.h和include/win32下的jawt_md.h、jni_md.h 

gcc -shared -o lover.dll -I "E:\soft\jdk1.8.0_271\include" -I "E:\soft\jdk1.8.0_271\include\win32" Demo.cpp

        5)重新编辑java代码,通过javac编译成.class文件。

public class Demo {
	public native String myMachine();
	
	static{
        //通过绝对路径加载动态链接库
		System.load("C:\\Users\\lenovo\\Desktop\\jni\\" + "lover.dll");
	}
	
	public static void main(String[] args) {
		String machine = new Demo().myMachine();
		System.out.println(machine);
	}
}

打印结果:

~jni>java -cp D:\jni Demo
this is elean's machine

        本文中只是简单的介绍了java通过JNI调用本地方法的实现方式。在实现过程中,笔者是直接使用mingW编译器通过命令行的方式实现的编译和执行,读者们也可以下载c/c++语言的IDE工具,在工具里面进行操作,笔者也很流畅的尝试了用codeblock进行编译的方式,希望大家也多多去实操一下,如果遇到什么问题,在留言里面互动促进相互学习。

        那么,JNI究竟如何对c语言进行包装才变成我们熟悉的java语系的呢?由于时间原因(主要笔者还没研究),就留待下篇文章唠吧,敬请期待!!!

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值