native Keyword in Java – Native Methods and JNI
The native keyword marks a method that is declared in Java but implemented in another language, usually C or C++. The method has no body in Java; at run time the JVM links it to a function in a native library through the Java Native Interface (JNI).
This tutorial explains the rules for native methods, walks through a complete working JNI example, shows native methods inside the JDK itself, and introduces the modern Foreign Function & Memory (FFM) API that replaces most hand-written JNI.
1. What Is a native Method?
A native method looks like an abstract method: a signature followed by a semicolon. The difference is that its implementation exists, just outside Java:
[access modifier] [static] native returnType methodName(parameters);
public native int add(int a, int b);
private static native long checksum(byte[] data);
Typical reasons to use native code:
- Accessing operating-system or hardware features that Java does not expose.
- Reusing existing C or C++ libraries (graphics, codecs, scientific code, drivers).
- Very performance-critical routines, although the modern JIT often makes this unnecessary.
2. native Methods Inside the JDK
You use native methods every day. Core parts of the JDK are implemented in native code inside the JVM:
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class NativeMethodsInJdk {
public static void main(String[] args) throws Exception {
check(Object.class.getMethod("hashCode"));
check(Object.class.getMethod("getClass"));
check(System.class.getMethod("currentTimeMillis"));
check(System.class.getMethod("nanoTime"));
check(System.class.getMethod("arraycopy", Object.class, int.class, Object.class, int.class, int.class));
check(String.class.getMethod("length"));
}
static void check(Method m) {
System.out.printf("%-35s native? %s%n",
m.getDeclaringClass().getSimpleName() + "." + m.getName() + "()",
Modifier.isNative(m.getModifiers()));
}
}
Object.hashCode() native? true Object.getClass() native? true System.currentTimeMillis() native? true System.nanoTime() native? true System.arraycopy() native? true String.length() native? false
3. A Complete JNI Example
Writing your own native method takes four steps: declare it in Java, generate a C header, implement it in C, and load the compiled library.
Step 1: Declare the native methods and load the library
public class HelloJNI {
static {
System.loadLibrary("hello"); // loads libhello.so / libhello.dylib / hello.dll
}
// Declared in Java, implemented in C
private native void sayHello(String name);
private native int add(int a, int b);
public static void main(String[] args) {
HelloJNI app = new HelloJNI();
app.sayHello("Asha");
System.out.println("3 + 4 = " + app.add(3, 4));
}
}
System.loadLibrary("hello") looks for libhello.dylib on macOS, libhello.so on Linux or hello.dll on Windows in the directories listed in java.library.path. It is usually placed in a static block so the library loads once, when the class is initialised.
Step 2: Generate the C header
javac -h . HelloJNI.java produces a header with the exact C function names and signatures the JVM expects:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */
#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloJNI
* Method: sayHello
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_HelloJNI_sayHello
(JNIEnv *, jobject, jstring);
/*
* Class: HelloJNI
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_HelloJNI_add
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
Step 3: Implement the functions in C
#include <jni.h>
#include <stdio.h>
#include "HelloJNI.h"
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject obj, jstring name) {
const char *cname = (*env)->GetStringUTFChars(env, name, NULL);
printf("Hello from C, %s!\n", cname);
fflush(stdout);
(*env)->ReleaseStringUTFChars(env, name, cname);
}
JNIEXPORT jint JNICALL Java_HelloJNI_add(JNIEnv *env, jobject obj, jint a, jint b) {
return a + b;
}
Every JNI function receives a JNIEnv* (the gateway to JVM services such as converting strings) and the object (jobject) or class (jclass, for static methods) it was called on. Java types map to C types such as jint, jstring and jobject.
Step 4: Build the library and run
# 1. Compile the Java class and generate the C header (HelloJNI.h)
javac -h . HelloJNI.java
# 2. Compile the C code into a shared library
# macOS
clang -shared -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/darwin" HelloJNI.c -o libhello.dylib
# Linux
gcc -shared -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" HelloJNI.c -o libhello.so
# Windows (MSVC): cl /LD /I"%JAVA_HOME%\include" /I"%JAVA_HOME%\include\win32" HelloJNI.c /Fehello.dll
# 3. Run, telling the JVM where the library is
java -Djava.library.path=. HelloJNI
Hello from C, Asha! 3 + 4 = 7
If the library cannot be found, the call fails with java.lang.UnsatisfiedLinkError: no hello in java.library.path.
4. Rules for native Methods
nativecan be applied only to methods, not to classes, fields or constructors.- A native method has no body; it ends with a semicolon.
- It cannot be
abstract(it has an implementation) and could not bestrictfpbefore Java 17. - It can be
static,final,synchronizedand have any access modifier. - It can be overridden by a Java method in a subclass, and can override a Java method.
- It can throw exceptions; native code raises them through
JNIEnvfunctions such asThrowNew.
5. Advantages and Disadvantages
| Advantages | Disadvantages |
|---|---|
| Access to OS and hardware features | Loses platform independence: a library must be built for every OS and CPU |
| Reuse of existing native libraries | A bug in native code can crash the entire JVM |
| Possible speed-ups for special workloads | No garbage collection or bounds checking in native memory; leaks are easy |
| Calls across the boundary have overhead; JNI code is verbose and hard to debug |
6. The Modern Alternative: Foreign Function & Memory API
Java 22 finalised the FFM API (java.lang.foreign, JEP 454). It calls native functions directly from Java, with no C glue code, no header generation and no native keyword:
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
public class FfmStrlen {
public static void main(String[] args) throws Throwable {
Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
try (Arena arena = Arena.ofConfined()) {
MemorySegment text = arena.allocateFrom("JavaByTechie");
long len = (long) strlen.invokeExact(text);
System.out.println("strlen = " + len); // strlen = 12
}
}
}
For new code that needs native libraries, prefer FFM (and the jextract tool that generates bindings from C headers). JNI remains supported, and since Java 24 the JVM warns when native access is used without --enable-native-access.
7. Summary
nativedeclares a method whose implementation is in C/C++ or another language.- JNI connects Java and native code: declare, generate a header with
javac -h, implement, thenSystem.loadLibrary. - Native methods have no body and cannot be abstract; they can be static, final or synchronized.
- Native code sacrifices portability and safety, so use it only when needed.
- On Java 22+, the FFM API is the simpler, safer way to call native libraries.
Related Tutorials
Frequently Asked Questions (FAQ)
Q1. What is the native keyword in Java?
A method modifier indicating that the method is implemented in native code, such as C or C++, rather than in Java.
Q2. What is JNI?
The Java Native Interface: the standard framework that lets Java code call native functions and lets native code create and use Java objects.
Q3. Can a native method have a body?
No. It ends with a semicolon, like an abstract method. The body lives in the native library.
Q4. Can native be used with classes or variables?
No. It is valid only on methods.
Q5. Can a native method be abstract?
No. abstract means no implementation exists, while native means an implementation exists in another language. The two are contradictory.
Q6. Can a native method be static?
Yes. The C function then receives a jclass instead of a jobject as its second parameter.
Q7. How does Java find the native implementation?
By name: Java_ClassName_methodName (with package names included), or by functions registered explicitly with RegisterNatives.
Q8. What does System.loadLibrary do?
It loads a native library by its base name from java.library.path, adding the platform's prefix and extension. System.load takes an absolute file path instead.
Q9. What causes UnsatisfiedLinkError?
The library could not be found or loaded, or it does not contain a function matching the native method's name and signature.
Q10. How do I generate a JNI header file?
Run javac -h <dir> MyClass.java. The older javah tool was removed in Java 10.
Q11. Is Object.hashCode() a native method?
Yes, along with getClass(), notify(), wait(long) and others in Object, and methods like System.currentTimeMillis() and System.arraycopy().
Q12. Are native methods faster than Java?
Not necessarily. The JIT compiler produces fast code, and each Java-to-native call has overhead. Native code helps mainly when using specialised libraries or hardware features.
Q13. Can native code crash the JVM?
Yes. A segmentation fault or memory corruption in native code can terminate the whole JVM, unlike a Java exception.
Q14. Can native methods throw Java exceptions?
Yes. Native code calls ThrowNew (or Throw) on JNIEnv; the exception is raised in Java once the native method returns.
Q15. Can a native method be overridden?
Yes. A subclass can override it with a normal Java method, and a native method can override a Java method.
Q16. Does using native code break platform independence?
Yes. The native library must be compiled separately for every operating system and CPU architecture the application runs on.
Q17. What is the Foreign Function and Memory API?
A Java 22+ API in java.lang.foreign for calling native functions and managing off-heap memory directly from Java, without JNI glue code. It is the recommended replacement for most JNI use.
Q18. What is JNIEnv?
A pointer to a table of JNI functions passed to every native method. Native code uses it to work with Java strings, arrays, objects, classes and exceptions.
Q19. Can a native method be synchronized?
Yes. The JVM acquires the object's (or class's) monitor before calling the native function, exactly as for a Java method.
Q20. What is --enable-native-access?
A JVM option that grants modules permission to use restricted native operations. Since Java 24, using JNI or FFM without it prints a warning, as part of making the platform "integrity by default".