The native modifier in Java marks a method whose implementation is supplied by code written in another language—usually C or C++. A typical example is Object.hashCode():
public native int hashCode();
Because the JVM cannot directly manipulate raw memory or hardware, such low-level work is delegated to platform-specific native libraries. The Java Native Interface (JNI) is the bridge that lets bytecode invoke these external routines.
How a native call is resolved
At runtime the JVM looks for a matching function inside a dynamically-linked library. The search path is controlled by java.library.path. When the library is found, the JVM maps the Java method to the native symbol whose name follows the JNI naming convention:
Java_<fully-qualified-class>_<method-name></method-name></fully-qualified-class>
Creating and invoking your own native method
The workflow can be summarized in five steps:
- Declare the method in Java
public final class Greeter {
static { System.loadLibrary("greeter"); }
private static native void greet();
public static void main(String[] args) { greet(); }
}
-
Generate the JNI header
javac Greeter.java
javah -jni Greeter
This producesGreeter.hwhich contains the function prototype. -
Implement the native function
Creategreeter.c: ``` #include "Greeter.h" #include <stdio.h>JNIEXPORT void JNICALL Java_Greeter_greet (JNIEnv *env, jclass clazz) { puts("Hello from C!"); }
-
Compile the shared library
On Windows with MinGW: ``` gcc -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" ^ -shared -o greeter.dll greeter.cOn Linux/macOS: ``` gcc -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" \ -shared -fPIC -o libgreeter.so greeter.c -
Run the Java program
Ensure the library directory is onjava.library.path: ``` java -Djava.library.path=. Greeter
Pitfalls of going native
- Portability loss – each target platform needs its own compiled library.
- Harder debugging – stack traces cross language boundaries.
- Security surface increase – native code can crash the JVM or corrupt memory.
- Deployment complexity – shipping extra binaries alongside the JAR.
Therefore, native code should be used only when Java APIs cannot provide the required functionality, such as direct hardware access, legacy library integration, or extreme performance tuning.