Java 中的本地方法

2025年8月5日 | 阅读 6 分钟

原生方法是在 Java 中声明的一个方法,其实际代码在 Java 环境之外运行,通常使用 C、C++ 或汇编语言编写。我们使用 native 关键字来标记一个方法,并且不在 Java 类中提供其方法体。相反,方法体实现在一个单独的库中,并在运行时加载到 Java 应用程序中。

原生方法的使用

  • 访问 Java 不直接支持的平台特定功能,例如在 Windows、Linux 或 macOS 上调用原生 API。
  • 重用用 C 或 C++ 编写的遗留代码,而无需用 Java 重写。
  • 通过用 C 或汇编等低级语言编写速度关键部分来提高性能。

然而,使用原生方法也意味着放弃 Java 的平台可移植性并增加复杂性。

原生方法的特性

  • 非平台独立: Java 是平台无关的,以其“一次编写,随处运行”的理念而闻名。但是,如果我们使用原生方法,我们的应用程序将变得平台相关——我们需要为 Windows、Linux、Mac 等系统准备单独的库。
  • 不能是抽象或 Strictfp: 原生方法不能是抽象的(因为它们没有方法体)。 此外,我们不能对原生方法使用 strictfp,因为浮点数的精度由原生代码处理。
  • 使用 JNI (Java Native Interface) 实现: Java 使用一种称为 JNI 的技术将 Java 代码与原生代码连接起来。JNI 允许 Java 调用用 C 或 C++ 编写的函数,并在原生代码中处理 Java 对象。
  • 连接 Java 与其他语言(如 C 或 C++): 原生方法充当 Java 和其他编程语言之间的桥梁。当我们需要使用 Java 不直接支持的功能时,例如系统级硬件或旧的 C 库,它会很有用。

使用 native 关键字

原生方法用 native 关键字标记。它告诉 Java:“这个方法的实际代码不是用 Java 编写的。”

示例

原生方法在 Java 中没有代码块——只有声明。逻辑是用另一种语言编写的,所以 Java 代码中没有 {}。

示例

存储在外部库中(.dll、.so、.dylib)

实际的原生代码保存在外部文件中

  • .dll 在 Windows 上
  • .so 在 Linux 上
  • .dylib 在 macOS 上

这些被称为共享库,Java 在运行时使用 System.loadLibrary("name") 加载它们。

在 Java 中实现原生方法

步骤 1:编写 Java 类并声明原生方法

这个 Java 程序使用了一个原生方法,这意味着该方法是用另一种语言(如 C)编写的,而不是用 Java 编写的。sayHello() 方法被标记为 native,在 Java 中没有代码。相反,Java 使用 System.loadLibrary() 加载一个名为“hello”的原生库。当程序运行时,它会调用 sayHello() 方法,Java 会查找并运行原生 C 库中的实际代码。当我们需要 Java 与用其他语言编写的代码协同工作时,这很有用。

步骤 2:在 C 中实现原生方法 (Main.c)

这段 C 代码用于使用 JNI (Java Native Interface) 将 Java 程序与原生 C 代码连接起来。它包含了三个头文件:

  • jni.h: 它允许 Java 和 C 交互,
  • stdio.h: 用于 printf() 函数,
  • Main.h: 它由 Java 类自动生成,包含正确的函数签名。

Java_Main_sayHello 函数由 Java 调用,以便 Java 知道要链接哪个函数。当 Java 运行此方法时,它会使用 printf 在控制台打印“Hello from native C code!”。这表明 Java 程序已成功调用并运行了原生 C 代码。

步骤 3:编译 Java 代码并生成头文件

此命令将 Java 文件编译为 .class 文件,并自动为原生方法生成一个 .h 头文件。

步骤 4:编译 C 文件

我们使用 gcc 编译器(来自 MinGW)将 C 文件转换为 Java 可以使用的 .dll 文件。在运行命令之前,我们设置 JAVA_HOME 路径,以便编译器知道 Java 的头文件在哪里。

它会生成一个名为 hello.dll 的文件。

步骤 5:运行 Java 程序

输出

Native Methods in Java

解释

当我们运行 Java 程序时,它会在屏幕上显示消息“Hello from native C code!”。此消息来自 C 代码,而不是来自 Java,这意味着 Java 成功找到并加载了 hello.dll,并通过 JNI 调用了原生方法。

原生方法的优点

  1. 更高的性能: 对于图像处理、加密或大型计算等任务,Java 可能会比较慢。原生代码(C/C++)在这些操作中运行速度更快。因此,当性能很重要时,使用 JNI 可以使我们的程序更快、更有效。
  2. 使用现有的 C/C++ 代码(遗留代码): 如果我们有大量用 C 或 C++ 编写的代码,则无需在 Java 中重写。JNI 允许我们在 Java 程序中重用这些代码。这可以节省时间和精力,特别是对于经过充分测试的原生库。

原生方法的缺点

  1. 更复杂的管理: 使用 JNI 意味着要处理两种语言——Java 和 C/C++。我们还需要处理头文件、原生编译器(如 GCC)和构建工具。这增加了额外的设置,并增加了出错的可能性,特别是对于初学者。
  2. 内存管理风险: Java 使用垃圾回收器自动管理内存,但原生代码(C/C++)则不。
  3. 安全问题: Java 被设计为安全的,但原生代码可能存在错误或漏洞。如果在原生部分发生任何问题(如缓冲区溢出),可能会影响整个应用程序。

结论

我们在 Java 中使用原生方法,这是一种与 C 或 C++ 等其他语言结合的明智方式。但它也会使程序更复杂且可移植性降低。Java 无法管理原生代码中的内存或安全性,因此我们必须谨慎。总之,原生方法功能强大,但应仅在真正需要时使用。

下载 Java 原生方法程序.zip

Java 原生方法选择题

1. 为什么我们应该在 Java 程序中使用原生方法?

  1. 使程序更难维护
  2. 避免使用内存
  3. 重用现有的 C/C++ 代码或访问系统级功能
  4. 简化调试
 

答案:C

解释: 当我们需要执行 Java 本身无法完成的任务时,原生方法非常有用,例如使用 C 代码调用系统函数或加速繁重的计算。


2. Java 中的 native 关键字表示什么?

  1. 该方法是从另一个类继承的
  2. 该方法是用另一种编程语言(如 C 或 C++)实现的。
  3. 该方法是抽象的
  4. 该方法是静态的
 

答案: B

解释: native 关键字告诉 Java 该方法的实际代码不是用 Java 编写的。它而是用另一种语言编写的,通常是 C 或 C++。Java 会在单独的库文件中(例如 Windows 上的 .dll)查找该代码。


3. 使用原生方法的一个缺点是什么?

  1. 它们使 Java 在所有情况下都更快
  2. 它们提高了跨平台的便携性
  3. 它们易于调试
  4. 它们降低了平台独立性,并且更难维护
 

答案: D

解释: 原生方法的一个主要缺点是它们打破了 Java 的“一次编写,随处运行”的规则。原生代码特定于操作系统,并且同时管理 Java 和原生语言可能很困难。


4. 用于供 Java 使用的原生代码创建的文件类型是什么?

  1. .java
  2. .class
  3. .dll, .so, .dylib
  4. .exe
 

答案:C

解释: Java 在运行时使用 System.loadLibrary("name") 加载这些文件。这些是共享库文件。


5. Java 的垃圾收集器管理原生代码中的内存?

  1. 不能
  2. 是的
  3. 仅在 Windows 上
  4. 仅用于静态方法
 

答案: A

解释: Java 的垃圾收集器仅管理 Java 对象使用的内存。


下一个主题null