- 性能优化:用 C 实现计算密集型或对性能要求极高的部分,用 Java 实现业务逻辑和界面。
- 硬件交互:通过 C 库直接与操作系统或硬件设备通信。
- 复用现有库:利用已有的、只有源码或二进制形式的 C/C++ 库。
实现 C 调用 Java 的核心技术是 Java Native Interface (JNI),下面我将提供一个详细的、分步的指南,教你在 Eclipse 中如何操作。

核心概念:JNI
JNI 是 Java 平台的一部分,它定义了一套规范,允许 Java 代码和其他语言(主要是 C/C++)进行交互,对于 C 调用 Java,流程如下:
- Java 端:定义一个
native方法(即一个声明但不提供具体实现的方法,告诉 JVM 这个方法由外部代码实现)。 - C 端:编写 C 代码来实现这个
native方法,C 代码需要包含 JNI 的头文件,并使用 JNI 提供的 API 来操作 Java 对象、调用 Java 方法等。 - 桥梁:JVM 在运行时会加载 C 实现的库(在 Windows 上是
.dll,Linux 上是.so,macOS 上是.dylib),并将 Java 方法的调用“转发”给 C 函数。
详细步骤:Eclipse CDT 环境下 C 调用 Java
我们将创建一个简单的示例:
- Java 程序有一个
native方法printHelloFromJava()。 - C 程序调用这个方法,并在控制台打印出从 Java 返回的字符串。
第 0 步:准备工作
- 安装 Eclipse:确保你安装了 Eclipse IDE for C/C++ Developers (CDT 版本),如果你只有 Eclipse for Java Developers,也可以通过 Help -> Install New Software -> "Eclipse CDT" 来安装 CDT 插件。
- 安装 JDK:确保你的系统已经安装了 JDK,
JAVA_HOME环境变量配置正确,Eclipse 需要用它来找到javac,javah(在 JDK 10+ 中被javac -h替代) 等工具。
第 1 步:创建 Java 项目并定义 native 方法
- 在 Eclipse 中,创建一个新的 Java Project (File -> New -> Java Project),命名为
JNITestJava。 - 在
src目录下创建一个 Java 类,com.example.NativeCaller。 - 在
NativeCaller.java中编写代码,定义一个native方法。
com/example/NativeCaller.java
package com.example;
public class NativeCaller {
// 1. 声明一个 native 方法
// 注意:必须是 public 或 protected,并且是 static 或非 static 的
public native void printHelloFromJava();
// 为了方便加载库,添加一个静态代码块
static {
// 2. 加载包含 C 实现的动态链接库
// System.loadLibrary 会根据操作系统自动寻找 libNativeCaller.so (Linux) 或 NativeCaller.dll (Windows)
System.loadLibrary("NativeCaller");
}
public static void main(String[] args) {
System.out.println("Java: 程序启动,准备调用 C 代码。");
NativeCaller caller = new NativeCaller();
// 3. 在 Java 中调用 native 方法
// 这会触发 C 代码的执行
caller.printHelloFromJava();
System.out.println("Java: C 代码调用完毕。");
}
}
第 2 步:生成 C 头文件 (.h)
这是连接 Java 和 C 的关键一步,我们需要根据 Java 类的 native 方法声明,生成一个 C 头文件,这个头文件包含了 JNI 需要的函数签名。

- 编译 Java 项目:右键点击项目 -> Build Project。
- 打开命令行(或使用 Eclipse 自带的终端 Terminal),切换到 Java 项目的根目录(即
JNITestJava目录)。 - 运行
javac和javah(或javac -h) 命令。
对于 JDK 8 及更早版本 (使用 javah):
# 1. 编译 .java 文件 javac -d . src/com/example/NativeCaller.java # 2. 生成 .h 文件 # -classpath . 指定类文件所在目录 # -jni 表示生成 JNI 标准的头文件 # com.example.NativeCaller 是完整的类名 javah -classpath . -jni com.example.NativeCaller
对于 JDK 10 及更新版本 (使用 javac -h):
# 1. 编译 .java 文件,并直接生成头文件到 include 目录 # -h 指定头文件输出目录 javac -d . -h include src/com/example/NativeCaller.java
执行后,你会在项目根目录下(或你指定的 include 目录下)找到一个头文件:com_example_NativeCaller.h。
com_example_NativeCaller.h (示例内容)

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_NativeCaller */
#ifndef _Included_com_example_NativeCaller
#define _Included_com_example_NativeCaller
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_NativeCaller
* Method: printHelloFromJava
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_NativeCaller_printHelloFromJava
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
这个文件非常重要!它定义了你的 C 函数必须使用的精确名称 Java_com_example_NativeCaller_printHelloFromJava 和参数 (JNIEnv *, jobject)。
第 3 步:创建 C/C++ 项目并实现 C 代码
我们使用 Eclipse CDT 来创建 C 项目并实现这个函数。
- 在 Eclipse 中,创建一个新的 C Project (File -> New -> C Project),命名为
JNITestC。 - 在 "Project type" 中选择 "Empty Project"。
- 创建完成后,右键点击项目 -> New -> Source File,命名为
NativeCaller.c。 - 将上一步生成的
com_example_NativeCaller.h文件复制到JNITestC项目的根目录下。 - 在
NativeCaller.c中包含这个头文件,并实现函数。
NativeCaller.c
#include <stdio.h>
#include "com_example_NativeCaller.h" // 包含我们生成的头文件
// 函数名必须和头文件中定义的完全一样
// 参数也必须一样: JNIEnv* 和 jobject
JNIEXPORT void JNICALL Java_com_example_NativeCaller_printHelloFromJava(JNIEnv *env, jobject obj) {
// JNI API 使用示例
// 1. 获取调用该方法的 Java 对象的 Class 对象
jclass clazz = (*env)->GetObjectClass(env, obj);
// 2. 查找要调用的 Java 方法的 ID
// 我们想调用 java.lang.System.out.println(String)
// 首先找到 System.out 对象
jclass system_class = (*env)->FindClass(env, "java/lang/System");
jfieldID out_field = (*env)->GetStaticFieldID(env, system_class, "out", "Ljava/io/PrintStream;");
jobject out_obj = (*env)->GetStaticObjectField(env, system_class, out_field);
// 然后找到 println 方法
jclass print_stream_class = (*env)->FindClass(env, "java/io/PrintStream");
jmethodID println_method = (*env)->GetMethodID(env, print_stream_class, "println", "(Ljava/lang/String;)V");
// 3. 创建一个 Java 字符串对象
jstring java_message = (*env)->NewStringUTF(env, "Hello from C! I am calling Java's println method.");
// 4. 调用 Java 的 println 方法
(*env)->CallVoidMethod(env, out_obj, println_method, java_message);
// 5. 释放本地引用 (最佳实践)
(*env)->DeleteLocalRef(env, java_message);
(*env)->DeleteLocalRef(env, out_obj);
(*env)->DeleteLocalRef(env, print_stream_class);
(*env)->DeleteLocalRef(env, system_class);
(*env)->DeleteLocalRef(env, clazz);
printf("C: Java native method has been called.\n");
}
第 4 步:配置 C 项目以编译成动态库
C 代码需要被编译成动态链接库,Java 才能加载它。
- 右键点击
JNITestC项目 -> Properties -> C/C++ Build -> Settings。 - 在 Tool Settings 标签页下:
- GCC C Compiler -> Includes:
- 在 Include paths (-I) 中,添加 JDK 的
include目录路径。C:/Program Files/Java/jdk-11.0.12/include(Windows) 或/usr/lib/jvm/java-11-openjdk-amd64/include(Linux)。 - 非常重要:如果你的系统是 64 位的,但 JDK 是 32 位的(或反之),你需要添加对应平台的子目录,如
win32或linux。C:/Program Files/Java/jdk-11.0.12/include/win32。
- 在 Include paths (-I) 中,添加 JDK 的
- GCC C Linker -> Libraries:
- 在 Library search path (-L) 中,添加 JDK 的
lib目录路径。C:/Program Files/Java/jdk-11.0.12/lib或/usr/lib/jvm/java-11-openjdk-amd64/lib。
- 在 Library search path (-L) 中,添加 JDK 的
- GCC C Linker -> Miscellaneous:
- 在 Linker flags 中,输入
-ljvm,这会告诉链接器去链接jvm库。
- 在 Linker flags 中,输入
- GCC C Compiler -> Includes:
- 应用并关闭。
右键点击 JNITestC 项目 -> Build Project,如果配置正确,它会在你的项目目录下(通常是 Debug 或 Release 文件夹)生成动态库文件:
- Windows:
NativeCaller.dll - Linux:
libNativeCaller.so - macOS:
libNativeCaller.dylib
第 5 步:运行 Java 程序并加载 C 库
这是最后一步,也是最容易出现问题的一步。
- 复制动态库:将上一步生成的动态库文件(如
NativeCaller.dll或libNativeCaller.so)复制到 Java 项目JNITestJava的根目录下。 - 配置 Java 运行时库路径:为了让 Java 虚拟机能找到这个动态库,你需要设置
java.library.path系统属性。- 方法一(推荐):修改 Java 项目的运行配置。
- 右键点击
JNITestJava项目 -> Run As -> Run Configurations... - 选择你的 Java Application。
- 在 Arguments 标签页的 VM arguments 文本框中输入:
-Djava.library.path="${workspace_loc:JNITestJava}/Debug"(如果你的库在
Release文件夹,就换成Release。${workspace_loc:...}是 Eclipse 宏,代表项目在 workspace 中的绝对路径)。
- 右键点击
- 方法二:在代码中设置(不推荐,因为路径可能不固定)。
System.setProperty("java.library.path", "path/to/your/library");
- 方法一(推荐):修改 Java 项目的运行配置。
- 运行:右键点击
JNITestJava项目中的NativeCaller.java-> Run As -> Java Application。
预期输出
如果一切顺利,你会在 Eclipse 的 Console 看到类似下面的输出:
Java: 程序启动,准备调用 C 代码。
Hello from C! I am calling Java's println method.
C: Java native method has been called.
Java: C 代码调用完毕。
常见问题与解决方法
-
java.lang.UnsatisfiedLinkError: no NativeCaller in java.library.path- 原因:JVM 找不到名为
NativeCaller的动态库。 - 解决:
- 确认动态库文件名是否正确(如
libNativeCaller.so或NativeCaller.dll)。 - 确认
java.library.path是否指向了包含该动态库的正确目录。 - 确认动态库文件是否确实存在于该目录中。
- 确认动态库文件名是否正确(如
- 原因:JVM 找不到名为
-
java.lang.UnsatisfiedLinkError: /path/to/libNativeCaller.so: libjvm.so: cannot open shared object file: No such file or directory- 原因:C 库在运行时需要链接
jvm库,但找不到它。 - 解决:在 C 项目的 Linker 设置中,确保
-ljvm被正确添加,Library search path 指向了 JDK 的lib目录。
- 原因:C 库在运行时需要链接
-
UnsatisfiedLinkError: ... printHelloFromJava ' (truncated) 'Wrong name'- 原因:C 函数的名称与
javah生成的头文件中的签名不匹配。 - 解决:仔细检查 C 函数名
Java_com_example_NativeCaller_printHelloFromJava是否完全正确,包括包名、类名和方法名的大小写和下划线。
- 原因:C 函数的名称与
-
找不到
jni.h- 原因:C 编译器找不到 JNI 的头文件。
- 解决:在 C 项目的 Compiler Includes 设置中,正确添加了 JDK 的
include目录路径。
-
64 位/32 位不匹配
- 原因:你的 Eclipse/Java 是 64 位的,但 C 编译器默认配置为 32 位(或反之),这会导致库无法加载。
- 解决:确保所有组件(Eclipse, JDK, CDT/GCC)的位数一致,在 C 项目的 Build Configurations 中,可以设置编译器为 32 位或 64 位模式(添加
-m32或-m64标志)。
通过以上步骤,你就可以在 Eclipse CDT 环境下成功实现 C 语言调用 Java 代码了,JNI 功能非常强大,但也相对复杂,尤其是在处理数据类型、异常和内存管理时,需要多加练习。
