概括
这篇博客将对java中的本地方法进行介绍,主要从为什么需要本地方法,以及本地方法是如何运行的两个方面进行。
为什么需要本地方法
java的跨平台特性是其一大优点,我们通过一次编译得到字节码,就能在任何可以运行jvm(Java Virtual Machine)的系统上执行。然而,在有些情况下,我们可能需要一些纯java代码无法实现的功能,这时我们需要一些在本地编译好,只能在特定环境下执行的代码,也就是本地方法,本地方法常常由C/C++语言编写。
需要使用本地方法的情况主要有以下三种:
- 需要和硬件进行交互
- 对于有些方法,使用C/C++可以加快执行的速度,因为C/C++的编译是依赖于操作系统的,对于程序性能有要求的可以考虑使用本地方法。
- 有些库已经用其他语言写好,我们想使用但是不想重新再写一遍的话,就可以使用本地方法。
java代码是在 jvm中运行的, 本地方法是在本地环境中运行的,为了在java程序中调用本地方法,就需要一个桥梁将本地方法和jvm连接起来,这个桥梁就是JNI(java native interface)。
本地方法是如何运行的
java关键字native
表明某个接口是由本地代码实现的。
java调用C/C++代码的时候,使用的是动态链接库的方式(shared libs),因为我们不能把字节码和本地编译好的代码放在同一个二进制文件中(jvm无法执行本地代码)。因此,最终编译好的java代码只声明对本地代码的引用, 而不是直接把代码复制过来。所以引用的库中会包含so/.dll/.dylib等文件,操作系统不同,文件的格式也不同。
本地方法也是一种抽象方法, 我们只是声明方法头, 具体的实现则交给本地代码。
1 | private native void aNativeMethod(); |
要实现本地方法的运行,主要需要以下组件:
- Java Code – our classes. They will include at least one native method.
- Native Code – the actual logic of our native methods, usually coded in C or C++.
- JNI header file – this header file for C/C++ (include/jni.h into the JDK directory) includes all definitions of JNI elements that we may use into our native programs.
- C/C++ Compiler – we can choose between GCC, Clang, Visual Studio, or any other we like as far as it’s able to generate a native shared library for our platform.
本地方法代码编写步骤
创建java类,其中声明本地方法
利用Java编译器(javac)的-h选项创建方法的引用,.h文件
创建.cpp文件,实现本地代码
编译并链接
编译C++代码:
Ubuntu version:
1 | g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux com_baeldung_jni_HelloWorldJNI.cpp -o com_baeldung_jni_HelloWorldJNI.o |
Windows version:
1 | g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 com_baeldung_jni_HelloWorldJNI.cpp -o com_baeldung_jni_HelloWorldJNI.o |
MacOS version;
1 | g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin com_baeldung_jni_HelloWorldJNI.cpp -o com_baeldung_jni_HelloWorldJNI.o |
和java共享库建立链接:
1 | g++ -shared -fPIC -o libnative.so com_baeldung_jni_HelloWorldJNI.o -lc |
Windows version:
1 | g++ -shared -o native.dll com_baeldung_jni_HelloWorldJNI.o -Wl,--add-stdcall-alias |
MacOS version:
1 | g++ -dynamiclib -o libnative.dylib com_baeldung_jni_HelloWorldJNI.o -lc |
在命令行中执行
1 | java -cp . -Djava.library.path=/NATIVE_SHARED_LIB_FOLDER com.baeldung.jni.HelloWorldJNI |
除此之外,java与本地方法之间还可以传递参数,参数类型是通过JNIEnv进行映射的,映射表的链接。