Java中的本地关键字是什么?

在玩这个谜题(这是一个Java关键字琐事游戏)时,我遇到了关键字。native

Java中的本机关键字用于什么?


答案 1

最小可运行示例

主要.java

public class Main {
    public native int square(int i);
    public static void main(String[] args) {
        System.loadLibrary("Main");
        System.out.println(new Main().square(2));
    }
}

Main.c

#include <jni.h>
#include "Main.h"

JNIEXPORT jint JNICALL Java_Main_square(
    JNIEnv *env, jobject obj, jint i) {
  return i * i;
}

编译并运行:

sudo apt-get install build-essential openjdk-7-jdk
export JAVA_HOME='/usr/lib/jvm/java-7-openjdk-amd64'
javac Main.java
javah -jni Main
gcc -shared -fpic -o libMain.so -I${JAVA_HOME}/include \
  -I${JAVA_HOME}/include/linux Main.c
java -Djava.library.path=. Main

输出:

4

在 Ubuntu 14.04 AMD64 上测试。还与Oracle JDK 1.8.0_45一起使用。

GitHub 上的示例供您使用。

Java 包/文件名中的下划线必须在 C 函数名称中进行转义,如中所述:在包含下划线的 Android 包名称中调用 JNI 函数_1

解释

native允许您:

  • 使用来自Java的任意汇编代码调用已编译的动态加载库(此处用C编写)
  • 并将结果返回到 Java 中

这可用于:

  • 在关键部分编写更快的代码,具有更好的CPU汇编指令(不是CPU可移植的)
  • 进行直接系统调用(非操作系统可移植)

权衡了较低的便携性。

您也可以从C调用Java,但您必须首先在C中创建一个JVM:如何从C++调用Java函数?

出于同样的原因,类似的本机扩展API也存在于许多其他“VM语言”中,例如PythonNode.jsRuby

安卓 NDK

在这种情况下,概念完全相同,只是您必须使用Android样板来设置它。

官方 NDK 存储库包含“规范”示例,例如 hello-jni 应用程序:

在 Android O 上使用 NDK 时,您可以看到与 下本机代码相对应的预编译代码。unzip.apk.solib/arm64-v8a/libnative-lib.so

TODO确认:此外,,说它是一个共享库,我认为这是AOT预编译的.dex对应于ART中的Java文件,另请参阅:Android中什么是ODEX文件?那么,也许Java实际上也是通过接口运行的?file /data/app/com.android.appname-*/oat/arm64/base.odexnative

OpenJDK 8 中的示例

让我们找到在jdk8u60-b27中定义的位置。Object#clone

我们将得出结论,它是通过调用实现的。native

首先我们发现:

find . -name Object.java

这就引出了 jdk/src/share/classes/java/lang/Object.java#l212

protected native Object clone() throws CloneNotSupportedException;

现在是困难的部分,在所有间接中找到克隆的位置。对我有帮助的查询是:

find . -iname object.c

这将找到可能实现Object的本机方法的C或C++文件。它把我们引向jdk/share/native/java/lang/Object.c#l47

static JNINativeMethod methods[] = {
    ...
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0]));
}

这就引出了这个符号:JVM_Clone

grep -R JVM_Clone

这将我们引向 hotspot/src/share/vm/prims/jvm.cpp#l580

JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
    JVMWrapper("JVM_Clone");

在扩展了一堆宏之后,我们得出的结论是,这是定义点。


答案 2

它标记了一个方法,它将用其他语言实现,而不是在Java中实现。它与JNI(Java Native Interface)一起工作。

过去使用本机方法来编写性能关键部分,但随着Java变得越来越快,现在这种情况已经不那么常见了。当前需要本机方法,当

  • 您需要从 Java 调用用其他语言编写的库。

  • 您需要访问只能从其他语言(通常为 C)访问的系统或硬件资源。实际上,许多与真实计算机(例如磁盘和网络IO)交互的系统函数只能执行此操作,因为它们调用本机代码。

另请参见 Java 本机接口规范