Category
language
Slug
call-c-from-java
Title
Calling out to C code from Java
Difficulty
advanced
Since JDK
22
Summary
Simplifies C/C++ code to be called from Java, allowing most existing libraries to be used without adapters or cumbersome C code modification.
Old code label
Java 1.1+
Old code
public class CallCFromJava {
static { System.loadLibrary("hello-jni"); }
public static native int greet(String name);
public static void main(String[] args) {
int ret = greet("Bambi");
System.out.println("Return value " + ret);
}
}
Then run javac -h, then view the generated .h file, then write your .c file:
#include "CallCFromJava.h"
JNIEXPORT int JNICALL Java_CallCFromJava_greet(JNIEnv *env, jclass this, jstring str) {
const char* name = (*env)->GetStringUTFChars(env, str, NULL);
printf("Hello %s\n", name);
return 0;
}
Modern code label
Java 22+
Modern code
public class CallCFromJava {
void main() throws Throwable {
try (Arena arena = Arena.ofConfined()) {
SymbolLookup ourLib =
SymbolLookup.libraryLookup("libhello-ffi.so", arena);
Optional<MemorySegment> segment = ourLib.find("greet");
MemorySegment foreignFuncAddr = segment.get();
FunctionDescriptor greet_sig= // return and input parameter
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
MethodHandle greetMethod =
Linker.nativeLinker().downcallHandle(foreignFuncAddr, greet_sig);
var ret = (long) greetMethod.invokeExact(arena.allocateFrom("Bambi"));
System.out.println("Return value " + ret);
}
}
}
C Code:
int greet(char* name) {
printf("Hello %s\n", name);
return 0;
}
Explanation
Java has two techniques for interfacing between Java and C/C++ code, the traditional JNI and the newer FFM. With JNI you simply declare your method as native, but then you have to run javac -h to generate a C-language .h (header) file, then have your C code implement that, and deal with the slightly cumbersome JNI C-language API.
FFM simplifies the C code to the point that it doesn't have to know anything about Java. This makes it much easier to interface with existing C/C++-language libraries. It does take a few more lines of Java code, but the net effect is more readable and more maintainable. Compare the C code in both versions and you'll see the difference!
Minimal runnable demo can be found at https://github.com/IanDarwin/javaevolved-java_callling_c
Why the modern way wins
👁 Better readability — C code doesn't have to learn Java!
⚡ More flexible - can directly call most existing C/C++ libraries
⚡ Easier - don't have to stop, run javac -h, and implement the "interface" defined in .h file.
Category
language
Slug
call-c-from-java
Title
Calling out to C code from Java
Difficulty
advanced
Since JDK
22
Summary
Simplifies C/C++ code to be called from Java, allowing most existing libraries to be used without adapters or cumbersome C code modification.
Old code label
Java 1.1+
Old code
Modern code label
Java 22+
Modern code
Explanation
Java has two techniques for interfacing between Java and C/C++ code, the traditional JNI and the newer FFM. With JNI you simply declare your method as native, but then you have to run
javac -hto generate a C-language .h (header) file, then have your C code implement that, and deal with the slightly cumbersome JNI C-language API.FFM simplifies the C code to the point that it doesn't have to know anything about Java. This makes it much easier to interface with existing C/C++-language libraries. It does take a few more lines of Java code, but the net effect is more readable and more maintainable. Compare the C code in both versions and you'll see the difference!
Minimal runnable demo can be found at https://github.com/IanDarwin/javaevolved-java_callling_c
Why the modern way wins
👁 Better readability — C code doesn't have to learn Java!
⚡ More flexible - can directly call most existing C/C++ libraries
⚡ Easier - don't have to stop, run javac -h, and implement the "interface" defined in .h file.