大家好,欢迎来到IT知识分享网。
文章目录
1. 简介
1.1 个人理解
初次遇见 native 是在 java.lang.String 源码中的一个 intern 方法:
public native String intern();
因为还是第一次遇到,所以就去搜了一些文章进行了解。下面就对一些 Native 关键字进行一些总结。
native 也即 JNI —— Java Native Interface(Java 本地接口)。凡是一种语言,都希望是纯的。比如解决某一个方案就单单使用同一个语言来实现。而 Java 却不然,Java 平台有个用户和本地 C 代码进行相互操作的 API,称为 Java Native Interface (Java 本地接口)。也就是说,相当于使用 Java 语言声明了一个方法,而这个方法的具体实现是在其他语言(如 C、C++等)中实现的,所以 Java 中编写的也就类似于一个接口,只是这个接口被称作本地接口。
Java 使用本地接口也是有原因的,因为 Java 的平台无关性,有优势当然也有牺牲,它的缺点就是不能使用 Java 代码直接对一些底层进行操作,但是对底层的操作又是一个语言必不可少的,于是 Java 就想到了间接去操作底层,而中间利用的就是操作系统。所以有些方法,Java 声明为了 native ,具体的实现是在 DLL 中,JVM 去进行真正的操作。
简单记忆:native 方法是 Java 中声明,由操作系统中具体方法实现。
1.2 其他介绍
网友见解:
- native 是与 C++ 联合开发的时候用的!java 自己开发不用的!
- 使用 native 关键字说明这个方法是原生函数,也就是这个方法是用 C/C++ 语言实现的,并且被编译成了 DLL,由 Java去调用。
这些函数的实现体在 DLL 中,JDK 的源代码中并不包含,你应该是看不到的。对于不同的平台它们也是不同的。这也是 Java 的底层机制,实际上 Java 就是在不同的平台上调用不同的 native 方法实现对操作系统的访问的。 - native 是用做 java 和其他语言(如c++)进行协作时用的也就是 native 后的函数的实现不是用 Java 写的,既然都不是 Java,那就别管它的源代码了,呵呵。
- native 的意思就是通知操作系统,这个函数你必须给我实现,因为我要使用。所以 native 关键字的函数都是操作系统实现的,Java 只能调用。
- Java 是跨平台的语言,既然是跨了平台,所付出的代价就是牺牲一些对底层的控制,而 Java 要实现对底层的控制,就需要一些其他语言的帮助,这个就是 native 的作用了
- Java 不是完美的,Java 的不足除了体现在运行速度上要比传统的 C++ 慢许多之外,Java 无法直接访问到操作系统底层(如系统硬件等),为此 Java 使用 native 方法来扩展 Java 程序的功能。
Java 本地方法适用的情况:
- 为了使用底层的主机平台的某个特性,而这个特性不能通过 Java API 访问;
- 为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用 Java 编写的;
- 为了加快程序的性能,而将一段时间敏感的代码作为本地方法实现。
2. 用 Java 调用 C 的实例
为了更好的理解 Java 中调用 Native 方法,特来编写一个具体的小的测试。
以下所有文件都存于个人本地文件夹:C:\Users\Eric\Desktop\NativeTest。
2.1 创建包含本地方法的类
在文件夹下创建一个 HelloNative.java
文件,里面包含着一个 native 的方法和加载库的方法 loadLibrary。代码如下:
public class HelloNative {
static {
// 注意加载库的名字为 HelloNative,需要与下文的生成文件保持一致
System.loadLibrary("HelloNative");
}
public static native void sayHello();
@SuppressWarnings("static-access")
public static void main(String[] args) {
new HelloNative().sayHello();
}
}
首先注意的是 native 方法,然后那个加载库的静态代码块在后面也起作用。native 关键字告诉编译器(其实是 JVM)调用的是该方法在外部定义,这里指的是 C。
2.2 编译运行
在当前文件夹下使用 CMD 命令行编译 HelloNative.java
,如下。
如果当前类中没有 Native 方法,那么我们可以直接使用 java 命令直接运行,但是此时大家直接运行这个代码,会出现以下结果:
意思是虚拟机说不知道如何找到 sayHello。因为我们定义的 sayHello 方法为 native 类型,所以我们还需要再进行下文的操作步骤。
2.3 获得头文件
在当前文件目录下运行 javah,得到包含该方法的 C 声明头文件 。命令如下:
javah HelloNative # 生成 .h文件
得到的结果如下:
得到的 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: sayHello * Signature: ()V */
JNIEXPORT void JNICALL Java_HelloNative_sayHello
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
这个头文件中可以看见我们声明的 Java 本地化 sayHello
方法,对应 C 的声明:JNIEXPORT void JNICALL Java_HelloNative_sayHello(JNIEnv *, jclass);
,我们只要实现这个方法即可。
注意:头文件中 jni.h 这个文件,是在本地 JDK 目录下的 include 文件夹中,例如我的目录:
2.3 C 实现头文件的声明方法
生成了头文件之后,我们再在当前文件夹下创建一个 HelloNative.c
文件,并简单地实现 HelloNative.h
文件中声明的 sayHello
方法,代码如下:
// 包含刚才生成的.h文件
#include "HelloNative.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_HelloNative_sayHello(JNIEnv *env, jclass thisClass) {
printf("Hello, Native!!");
}
结果:
2.4 生成动态链接库
到了这一步,我们需要将上述两个文件 HelloNative.c
和 HelloNative.h
编译为动态链接库。
这里说明两种方法:分步编译或一次性编译形成动态链接库文件。
(1)一次性编译
在 Windows CMD 命令行里,使用如下命令:
gcc -m64 -Wl,--add-stdcall-alias -I"C:/Program Files/Java/jdk1.8.0_181/include" -I"C:/Program Files/Java/jdk1.8.0_181/include/win32" -shared -o HelloNative.dll HelloNative.c
注意:上述 JDK 为个人本地路径,需要根据个人情况进行修改。-m64
表示生成 dll
库是 64 位的,参数 -I
指定头文件路径上述命令运行后,我们会在目录文件夹下生成相应的动态链接库文件,如下:
如果使用的 Windows 上面没有 gcc,需要先下载压缩包然后配置一下环境变量即可使用(两分钟就搞定),压缩包及配置步骤,这里推荐一条博文:解决mingw-w64外网下载太慢问题,离线包安装配置过程讲解
(2)分步编译
为了演示分步编译,我们先把上面一次性编译生成的动态链接库文件 HelloNative.dll
给删除掉。然后再执行如下命令:
$ gcc -c -I"C:/Program Files/Java/jdk1.8.0_181/include" -I"C:/Program Files/Java/jdk1.8.0_181/include/win32" HelloNative.c HelloNative.h
结果如下:
在这里我们只需要先关注生成的 HelloNative.o
文件,然后我们执行第二步骤的命令,将该文件编译为 dll
文件:
$ gcc -Wl,--add-stdcall-alias -shared -o HelloNative.dll HelloNative.o
结果如下:
2.5 再次运行Java类
使用 2.4 中的任意一种方法生成动态链接库 HelloNative.dll
文件后,我们再次使用 java 命令运行 Java 类,结果如下:
我们可以看到 Java 类已经可以成功运行了,并且我们也可以看出它运行的实际是我们使用 C 语言编写的实现方法,它作为本地方法 native 来被 Java 代码调用。
2.6 总结
可以将 native 方法比作 Java 程序同C程序的接口,其实现步骤:
- 在 Java 中声明 native 方法,然后编译成 .class 文件;
- 用 javah 产生一个 .h 文件;
- 写一个 .c 或 .cpp 文件实现 native 导出方法,其中需要包含第二步产生的 .h 文件(注意其中又包含了JDK带的jni.h文件);
- 将第三步的 .c 或 .cpp 文件编译成动态链接库文件;
- 在 Java 中用 System.loadLibrary() 方法加载第四步产生的动态链接库文件,这个 native 方法就可以在 Java 中被访问了。
JNI 调用 C 流程图:
参考文章
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/15667.html