当前位置: 首页 > news >正文

Java Native Interface 使用指南

我们知道Java本身的实现,很大一部分是用C++写的。实际上,Java也允许我们和原生平台的代码进行交互。

Java定义了一个接口规范,就叫做Java Native Interface,通过这个接口规范,我们就可以让Java代码运行原生平台的代码。

首先写一个Java的Class1:

public class HelloJNI {static {System.loadLibrary("hello"); // Load native library at runtime// hello.dll (Windows) or libhello.so (Unixes)}// Declare a native method sayHello() that receives nothing and returns voidprivate native void sayHello();// Test Driverpublic static void main(String[] args) {new HelloJNI().sayHello();  // invoke the native method}
}

从上面的代码,我们看到一些平常写Java代码的时候不常看见的东西,首先是这个:

 System.loadLibrary("hello");

这个System.loadLibrary方法可以用来调用平台的原生库。然后是这个:

private native void sayHello();

这个native关键字意味着这个代码的实现是原生平台实现,而不是Java代码。因此我们要做一个hello库,提供这个sayHello方法。

接下来我们用javah命令生成.h文件:

$ ls HelloJNI.java
HelloJNI.java
$ javah HelloJNI
$ ls *.h
HelloJNI.h

我们得到了HelloJNI.h文件,它的内容是根据HelloJNI.java生成的,我们看看里面的内容:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/** Class:     HelloJNI* Method:    sayHello* Signature: ()V*/
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject);#ifdef __cplusplus
}
#endif
#endif

可以看到上面的文件里自动生成了JNI接口规范的一些约定代码。比如jni.h这个是由JDK提供的,我们稍后要引用。还有sayHello对应的C/C++的方法是Java_HelloJNI_sayHello,这个命名方式可以看出来是Java_ClassName_MethodName,这个也是JNI的约定。

接下来我们写一个HelloJNI.c来实现这个HelloJNI.h

#include <jni.h>
#include <stdio.h>
#include "HelloJNI.h"// Implementation of native method sayHello() of HelloJNI class
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {printf("Hello World!\n");return;
}

这个代码就非常简单,就是实现了Java_HelloJNI_sayHello,功能就是printf("Hello World!\n");。我们编译这个代码:

$ c++ -c -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/darwin" HelloJNI.c

注意我是在MacOS上编译,所以引用的是MacOS的JDK的include目录,此外MacOS的内核名叫darwin,你如果用Linux系统,你的JDK的include目录里应该有一个linux目录,对应上面的darwin目录。

这两个-I主要是为了引用JNI相关的库文件:

$ ls $JAVA_HOME/include/jni*
/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/include/jni.h
$ ls $JAVA_HOME/include/darwin/jni*
/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/include/darwin/jni_md.h

这两个.h文件是必须的。编译完成后,我们就得到了object文件:

$ ls *.o
HelloJNI.o

nm命令看一下这个object file里面的符号:

$ nm HelloJNI.o
0000000000000000 T _Java_HelloJNI_sayHelloU _printf

可以看到包含的的methods。我们要把这个object文件链接成library,在MacOS下命令如下:

$ c++ -dynamiclib -o libhello.jnilib HelloJNI.o

这样我们就得到了Java可以调用的libhello.jnilib

$ ls *.jnilib
libhello.jnilib

我们在Java代码里写的是System.loadLibrary("hello"),但是这里的library名字叫做libhello,前面多了lib,这是一种命名约定。

如果你在Linux系统下,生成library的命令不太一样,需要用下面的命令生成.so文件,也就是shared library文件:

cc -shared -fpic -o libhello.so -I/usr/java/include -I/usr/java/include/linux HelloJNI.c

接下来就是编译Java代码:

$ javac HelloJNI.java

我们得到class文件:

$ ls *.class
HelloJNI.class

此时目录里应该有我们的library还有class文件:

$ ls *.class *.jnilib
HelloJNI.class  libhello.jnilib

此时我们可以使用java命令运行HelloJNI.class

$ java HelloJNI
Hello World!

可以看到我们这个Hello World!字符串是来自于native method的Java_HelloJNI_sayHello里面的printf。我们的Java代码与原生平台进行了交互。

参考资料:

  1. https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.htm

http://www.lryc.cn/news/373123.html

相关文章:

  • 代码随想录算法训练营第三十九天 | 62.不同路径、63. 不同路径 II、343. 整数拆分、96.不同的二叉搜索树
  • C/C++函数指针、C#委托是什么?
  • 红队攻防渗透技术实战流程:组件安全:JacksonFastJsonXStream
  • Perl 语言学习进阶
  • LangGraph实战:从零分阶打造人工智能航空客服助手
  • R可视化:R语言基础图形合集
  • mysql导入sql文件失败及解决措施
  • JS:获取鼠标点击位置
  • 使用开源的zip.cpp和unzip.cpp实现压缩包的创建与解压(附源码)
  • npm 异常:peer eslint@“>=1.6.0 <7.0.0“ from eslint-loader@2.2.1
  • Docker|了解容器镜像层(2)
  • 使用Python爬取temu商品与评论信息
  • mybatis学习--自定义映射resultMap
  • Elasticsearch之写入原理以及调优
  • python中装饰器的用法
  • php实现一个简单的MySQL分页
  • 算法训练营day23补签
  • 国密SM2JS加密后端解密
  • Cheat Engine.exe修改植物大战僵尸阳光与冷却
  • python内置模块之queue(队列)用法
  • Spring Security——结合JWT实现令牌的验证与授权
  • Vector的底层结构剖析
  • 实现抖音视频滑动功能vue3+swiper
  • Linux文件系统【真的很详细】
  • JAVA学习笔记DAY5——Spring_Ioc
  • WPF中的隧道路由和冒泡路由事件
  • ISO七层模型 tcp/ip
  • MySQL的三种重要的日志
  • 神经网络学习2
  • Spring Boot整合Redis通过Zset数据类型+定时任务实现延迟队列