核心概念
native 是 Java 中的一个修饰符,用于声明一个方法,当一个方法被声明为 native 时,它意味着:

这个方法的实现不是用 Java 代码编写的,而是用其他语言(最常见的是 C 或 C++)实现的,并且这些代码是平台相关的(Platform-Specific)。
native 关键字是 Java 与其他语言(主要是本地代码)进行交互的“桥梁”或“接口”。
为什么需要 native 关键字?(它的用途)
Java 的设计理念是“一次编写,到处运行”(Write Once, Run Anywhere),通过 Java 虚拟机实现了跨平台性,在某些特定场景下,纯 Java 代码无法满足需求,这时就需要 native 方法:
-
访问系统底层资源
(图片来源网络,侵删)- Java 被设计为一种安全的、受限制的语言,不能直接操作内存地址、硬件端口等底层资源,但有时必须这样做,
- 硬件交互:直接与显卡、声卡、网卡等硬件通信。
- 操作系统功能:调用操作系统的特定功能,如 Windows 的注册表操作、Linux 的某些内核特性。
- 内存管理:进行高性能的内存操作,如 C 语言中的指针操作。
- Java 被设计为一种安全的、受限制的语言,不能直接操作内存地址、硬件端口等底层资源,但有时必须这样做,
-
性能关键型代码
- 对于计算密集型任务,如物理引擎、图形渲染、音视频编解码等,用 C/C++ 实现通常比 Java 快得多,可以将这些核心算法用 C/C++ 编写,然后通过
native方法在 Java 中调用。
- 对于计算密集型任务,如物理引擎、图形渲染、音视频编解码等,用 C/C++ 实现通常比 Java 快得多,可以将这些核心算法用 C/C++ 编写,然后通过
-
复用现有库
- 很多成熟的、高性能的库(如科学计算库、加密库、数据库驱动等)都是用 C/C++ 编写的,通过
native方法,Java 程序可以无缝地调用这些现有的本地库,而无需重新用 Java 实现一遍。
- 很多成熟的、高性能的库(如科学计算库、加密库、数据库驱动等)都是用 C/C++ 编写的,通过
-
集成遗留系统
- 在一些大型企业中,可能存在大量用 C/C++ 编写的遗留代码,为了保护投资并让新的 Java 系统能够利用这些旧代码,
native方法是一种有效的集成方式。
- 在一些大型企业中,可能存在大量用 C/C++ 编写的遗留代码,为了保护投资并让新的 Java 系统能够利用这些旧代码,
native 方法的特点
-
没有方法体
native方法的声明中只有方法签名,没有用 包围的实现代码,分号 表示方法的结束。public native void sayHello();
-
与平台相关
native方法的实现代码(C/C++)需要为不同的操作系统(如 Windows, Linux, macOS)和不同的 CPU 架构(如 x86, ARM)分别编译,这意味着你的 Java 应用程序打包时,可能需要包含不同平台的本地库文件(.dllfor Windows,.sofor Linux,.dylibfor macOS)。
-
性能与安全性的权衡
- 优点:可以突破 Java 的性能限制,访问底层资源。
- 缺点:牺牲了 Java 的跨平台性(需要为不同平台提供库),并且引入了不安全性(本地代码可以绕过 JVM 的安全机制,导致内存泄漏、段错误等问题),调试本地代码通常比调试 Java 代码更困难。
如何使用 native 方法(一个完整的例子)
使用 native 方法通常包括以下几个步骤:
步骤 1:在 Java 代码中声明 native 方法
在一个 Java 类中声明你需要的 native 方法。
// NativeDemo.java
public class NativeDemo {
// 声明一个 native 方法
public native void displayMessage();
// 加载包含本地代码的库
static {
// System.loadLibrary() 会尝试加载名为 "NativeDemo" 的库
// 在 Windows 上是 NativeDemo.dll,在 Linux 上是 libNativeDemo.so
System.loadLibrary("NativeDemo");
}
public static void main(String[] args) {
new NativeDemo().displayMessage();
}
}
注意 static 代码块:System.loadLibrary("NativeDemo") 用于加载本地库,JVM 会在系统路径(如 Windows 的 PATH,Linux 的 LD_LIBRARY_PATH)中查找这个库,这个调用必须在调用任何 native 方法之前执行。
步骤 2:生成 C/C++ 头文件(.h)
你需要一个工具来将 Java 类中的 native 方法声明转换为 C/C++ 的函数原型,这个工具就是 javah(在旧版 JDK 中)或 javac -h(在新版 JDK 中,推荐使用)。
- 首先编译 Java 文件:
javac NativeDemo.java
- 然后使用
javac -h生成头文件:javac -h . NativeDemo.java
这会在当前目录下生成一个名为
com_example_NativeDemo.h的文件(如果你的类在com.example包下),我们简化一下,假设没有包,生成的文件就是NativeDemo.h。
NativeDemo.h 的内容大致如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h> /* Header for JNI */
/* Header for class NativeDemo */
#ifndef _Included_NativeDemo
#define _Included_NativeDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: NativeDemo
* Method: displayMessage
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_NativeDemo_displayMessage
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
关键点解释:
#include <jni.h>:JNI (Java Native Interface) 的头文件,定义了与 JVM 交互所需的所有数据结构和函数。JNIEXPORT:一个宏,表示该函数可以被外部(JVM)调用。JNICALL:一个宏,定义了函数的调用约定。Java_NativeDemo_displayMessage:这是 C/C++ 函数的命名规则,由包名_类名_方法名组成,如果没有包,类名_方法名。(JNIEnv *, jobject):这是所有native方法的标准参数。JNIEnv *:一个指向 JNI 环境指针的指针,通过这个指针,你可以在 C/C++ 代码中调用 JNI 函数,例如创建 Java 对象、访问 Java 字段、调用 Java 方法等。jobject:一个指向NativeDemo对象本身的引用,如果方法是static的,这个参数的类型会是jclass,代表NativeDemo的Class对象。
步骤 3:用 C/C++ 实现本地方法
创建一个 C/C++ 文件(NativeDemo.c),并实现 NativeDemo.h 中声明的函数。
// NativeDemo.c
#include <stdio.h>
#include "NativeDemo.h" // 包含刚才生成的头文件
// 实现 Java_NativeDemo_displayMessage 函数
JNIEXPORT void JNICALL Java_NativeDemo_displayMessage(JNIEnv *env, jobject obj) {
printf("Hello from C! This message is printed by a native method.\n");
// 你还可以通过 env 指针来操作 Java 对象
// 获取对象的类,然后调用其方法或访问字段
}
步骤 4:编译 C/C++ 代码为本地库
你需要使用 C/C++ 编译器(如 GCC 或 Clang)将 .c 文件编译成动态链接库。
-
在 Linux 上 (使用 GCC):
gcc -shared -fpic -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -o libNativeDemo.so NativeDemo.c-shared: 生成共享库(.so 文件)。-fpic: 生成位置无关代码,这是共享库的要求。-I: 指定 JNI 头文件的路径(${JAVA_HOME}是你的 JDK 安装路径)。-o: 指定输出的库名。
-
在 Windows 上 (使用 MinGW/GCC):
gcc -shared -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -o NativeDemo.dll NativeDemo.c
步骤 5:运行 Java 程序
- 确保
libNativeDemo.so(Linux) 或NativeDemo.dll(Windows) 位于系统能找到的路径中。- Linux: 将
.so文件放到/usr/lib或LD_LIBRARY_PATH指定的目录。 - Windows: 将
.dll文件放到与NativeDemo.class相同的目录,或者添加到系统的PATH环境变量中。
- Linux: 将
- 运行 Java 程序:
java NativeDemo
预期输出:
Hello from C! This message is printed by a native method.
| 特性 | 描述 |
|---|---|
| 关键字 | native |
| 作用 | 声明一个方法,其实现由非 Java 语言(如 C/C++)提供。 |
| 方法体 | 没有,以分号 结束。 |
| 跨平台性 | 否,需要为不同平台编译对应的本地库(.dll, .so, .dylib)。 |
| 性能 | 可以实现高性能,但也引入了本地代码的复杂性。 |
| 安全性 | 较低,本地代码可以绕过 JVM 的安全管理器。 |
| 主要用途 | 访问硬件、调用系统 API、性能优化、复用现有 C/C++ 库。 |
| 核心机制 | Java Native Interface (JNI),是连接 Java 和本地代码的官方规范。 |
native 关键字是一个强大的工具,但它是一把“双刃剑”,它为 Java 开发者打开了通往底层世界的大门,但也带来了平台依赖、安全风险和调试困难等挑战,它应该被谨慎使用,仅在必要时才被引入到 Java 项目中。
