从 Java 调用 C 代码
首页
/
Language
/
从 Java 调用 C 代码
Language
高级
从 Java 调用 C 代码
贡献
🐛 报告代码问题
🌐 报告翻译问题
💡 建议新模式
FFM 让 Java 直接调用 C 库,无需 JNI 样板代码或在 C 端了解 Java。
Foreign Function & Memory API
Native Code
代码对比
✕ Java 1.1+
复制
public class CallCFromJava {
static { System.loadLibrary("strlen-jni"); }
public static native long strlen(String s);
public static void main(String[] args) {
long ret = strlen("Bambi");
System.out.println("Return value " + ret); // 5
}
}
// Run javac -h to generate the .h file, then write C:
// #include "CallCFromJava.h"
// #include
// JNIEXPORT jlong JNICALL Java_CallCFromJava_strlen(
// JNIEnv *env, jclass clazz, jstring str) {
// const char* s = (*env)->GetStringUTFChars(env, str, NULL);
// jlong len = (jlong) strlen(s);
// (*env)->ReleaseStringUTFChars(env, str, s);
// return len;
// }
✓ Java 22+
复制
void main() throws Throwable {
try (var arena = Arena.ofConfined()) {
// Use any system library directly — no C wrapper needed
var stdlib = Linker.nativeLinker().defaultLookup();
var foreignFuncAddr = stdlib.find("strlen").orElseThrow();
var strlenSig = FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
var strlenMethod = Linker.nativeLinker() .downcallHandle(foreignFuncAddr, strlenSig);
var ret = (long) strlenMethod.invokeExact(arena.allocateFrom("Bambi"));
System.out.println("Return value " + ret); // 5
}
}
// Your own C library needs no special Java annotations:
// long greet(char* name) {
// printf("Hello %s\n", name);
// return 0;
// }
分享
𝕏
🦋
in
⬡
发现此代码有问题? 告诉我们。
为什么现代方式更好
👁
C 代码保持纯 C
C 函数无需 JNI 注解或 JNIEnv 样板——任何现有的 C 库都可以直接调用。
⚡
更灵活
无需编写适配器代码或生成头文件,即可直接调用大多数现有的 C/C++ 库。
🛠️
工作流更简便
无需停下来运行 javac -h,也无需实现生成的 .h 文件中定义的接口。
旧方式
JNI(Java 原生接口)
现代方式
FFM(外部函数与内存 API)
自 JDK
22
难度
高级
JDK 支持
从 Java 调用 C 代码
可用
在 JDK 22 中标准化(2024 年 3 月);此前自 JDK 14 起处于孵化阶段
工作原理
Java 有两种调用原生 C/C++ 代码的方式:传统的 JNI 和现代的 FFM API。使用 JNI,需要将方法声明为 native,运行 javac -h 生成 C 头文件,然后使用繁琐的 JNI C API(JNIEnv、jstring 等)实现函数。FFM 在 Java 22 中作为标准 API 引入,消除了这一切:C 代码就是普通的 C——无需 JNI 约定。这使得调用现有 C/C++ 库无需修改变得更加容易。Java 端使用 Arena 进行安全的堆外内存管理,使用 MethodHandle 进行降调用,确保灵活性和安全性。
相关文档
JEP 454: Foreign Function & Memory API ↗
java.lang.foreign package (Java 22) ↗
证明
查看证明源码 ↗
相关模式
I/O
高级
File memory mapping
Java 8
try (FileChannel channel =
FileChannel.open(path,
StandardOpenOption.READ,
StandardOpenOption.WRITE)) {
MappedByteBuffer buffer =
channel.map(
FileChannel.MapMode.READ_WRITE,
0, (int) channel.size());
// Limited to 2GB
// Freed by GC, no control
}
Java 22+
FileChannel channel =
FileChannel.open(path,
StandardOpenOption.READ,
StandardOpenOption.WRITE);
try (Arena arena = Arena.ofShared()) {
MemorySegment segment =
channel.map(
FileChannel.MapMode.READ_WRITE,
0, channel.size(), arena);
// No size limit
// ...
} // Deterministic cleanup
悬停查看现代版 ➜
JDK 22+
→
Language
入门
Compact source files
Java 8
public class HelloWorld {
public static void main(String[] args) {
System.out.println(
"Hello, World!");
}
}
Java 25
void main() {
IO.println("Hello, World!");
}
悬停查看现代版 ➜
JDK 25+
→
Language
入门
Unnamed variables with _
Java 8
try {
parse(input);
} catch (Exception ignored) {
log("parse failed");
}
map.forEach((key, value) -> {
process(value); // key unused
});
Java 22+
try {
parse(input);
} catch (Exception _) {
log("parse failed");
}
map.forEach((_, value) -> {
process(value);
});
悬停查看现代版 ➜
JDK 22+
→