Week 18 — What is the native keyword in Java?
Question of the Week #18
What is the native keyword in Java?
13 Replies
native keyword points on method that it was implemented on native language with interface JNI
example -> public native int result(int a, int b);
Submission from Piotr Łyszkowski#0456
Native means that it is implementing non java code like c++ or c. This could be literally anything, if you would want to access a low level system function like speakers and could be implemented like this:
public class SoundPlayer {
public native void playSoundEffect(String path);
}
If it is not marked with native it will look for the method in the Java bytecode and it would not find it because it's not in the java bytecodeSubmission from Lieutenant Piplup#9658
It's a keyword that is used to indicate a method implemented for a specific platform using native code, which is most probably written in C or C++. Methods with the
native
keyword are not written Java but in the language that the shared library provided for the class, which allows it to compile directly into machine code and interact with system resources.
Here's an example code:
Submission from Fluid#2654
"native" Keyword in java means a method is natively executed
Submission from imraklr#1712
Native keyword is used to declare a method as Native ( method implementation is present in a different language).
Submission from ᴀηωєѕн#7940
t means that method implementation is present in the different language
it*
Submission from Error_#1345
The
native
keyword is only used on method declarations. In simple terms, it means that the implementation of the method has not been done in Java only, but also in C or C++, which makes these methods platform-dependant, unlike java code which the JVM can run. Therefore, we can not see the bodies of these methods in the source files, and methods with the native keyword can not have bodies and must end with a semi-colon(;
). We can also use them to invoke C++ libraries. For example:
public class DateTimeUtils {
public native String getSystemTime();
static {
System.loadLibrary("nativedatetimeutils");
}
}
Submission from Luqmaan#0205
The native keyword can appear on methods and implies that they reference a method from a native library (.dll, .so) instead. Methods with the native keyword don't have the
code
attribute, which means that they don't have associated java code. Instead, they get linked to a native library loaded by System.load()
or System.loadLibrary()
. Libraries loaded by either of those 2 methods have to use JNI and have to have exported methods named in a way java understands, we'll look at the names later.
This has the benefit of allowing java to call to native code, which can then use actual C libraries to do native system things. Java itself often uses native to call back to the java runtime itself, or to implement platform-specific functionality.
Example:
Running javac -h . JniExample.java
outputs this header file to use from C++ code, which has all the native method headers we need to implement ourselves:
This is the format we discussed earlier: Java_packageName_className_methodName
. There are also some predefined arguments we need to take into account, a pointer to JNIEnv, the runtime environment, and the class the method is defined in.
Implementing this looks like this: (see next message)
After compiling this:
1.
$ g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux/ main.cpp
2. $ g++ -shared -fPIC -o libexample.so main.o -lc
We get a libexample.so
, which is the library we need.
Now we can run our original example: java example.JniExample -Djava.library.path=.
, after which we will get "Hello chat" printed to the terminal. We successfully linked a native library to a java program!
Important to note is the java.library.path
variable, which tells java where to find native libraries. Currently, we're just giving it the current directory, which may not always be what you want. As long as your libexample.so
file is in the same directory as where you're running java
from, this will work.⭐ Submission from 0x150#9699
JNI allows to interact with native code from Java. For this, methods implemented natively need to be created using the
native
keyword. These methods cannot have a method body.
In order to then use this natively implemented method, one needs to call System.loadLibrary()
in order to load the native library (which loads a .so
or .dll
file). This is usually done in a static
block.
A class with a native method could look like this:
When using JNI, it is recommended to keep the native methods private
and only allow them to be accessed via other non-native methods. These can then provide additional validation (which can also be added at a later point in time).
In order to create the actual native implementation, javac
is capable of generating C header files using the .h
argument. We can compile the class using the following command:
Then, javac
does not just create a NativeDemo.class
file but also a NativeDemo.h
file as follows:
This is a C header file which is a bit similar to an interface. It declares a method without providing an implementation. It is then possible to provide an implementation of that method in a language like C or C++.
JNI requires maintaining a significant amount of non-Java code, even if one just wants to call a single native method.
The Foreign Function & Memory API provides a way to call native code from Java without needing to maintain lots of additional native code just for the interaction between Java and native code.
As of Java 20, the Foreign Function & Memory API is a preview feature (JEP 434: https://openjdk.org/jeps/434) of Java meaning it is fully implemented and tested but still subject to change.
As of the time of writing, a third Preview of the Foreign Function & Memory API (JEP 442: https://openjdk.org/jeps/442) is proposed to target Java 21. However, this JEP is (at the time of writing) not finished and might still be edited.When the Foreign Function & Memory API is enabled, it is possible to call a native method like this:
This first obtains a
Linker
object which is responsible for finding native code (1).
Then, a SymbolLookup
object is loaded (2), which can load functions from the standard library using the Linker
object from before.
After that, a MethodHandle
is created for the native call (3). A method handle represents a some executable code (typically a method).
In order to obtain this method handle, the SymbolLookup
object is used for finding the method to call (in this case strlen
) (3.1).
The signature of the loaded method needs to be specified using a FunctionDescriptor
(3.2). In this case, it represents a function returning a long
with a single reference parameter.
Since the parameter of the function should be located outside the Java heap, an Arena
object is created which will deallocate it when closed (4). As soon as the try
-block terminates, all memory allocated by that Arena
object will be deallocated.
After obtaining the Arena
object, it is possible to take a Java String ("Hello World"
) and convert it to a native String using the Arena#allocateUtf8String
method (5).
When all of that is done, it is possible to call the function using the invoke
method of the MethodHandle
passing the native String as an argument.⭐ Submission from dan1st#7327