Week 18 — What is the native keyword in Java?

Question of the Week #18
What is the native keyword in Java?
13 Replies
Eric McIntyre
Eric McIntyre2y ago
native keyword points on method that it was implemented on native language with interface JNI
Eric McIntyre
Eric McIntyre2y ago
example -> public native int result(int a, int b);
Submission from Piotr Łyszkowski#0456
Eric McIntyre
Eric McIntyre2y ago
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 bytecode
Submission from Lieutenant Piplup#9658
Eric McIntyre
Eric McIntyre2y ago
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:
public class Main {
static {
System.loadLibrary("CoolLibrary");
}

public static native void coolMethod();

public static void main(String[] args) {
Main.coolMethod();
}
}
public class Main {
static {
System.loadLibrary("CoolLibrary");
}

public static native void coolMethod();

public static void main(String[] args) {
Main.coolMethod();
}
}
Submission from Fluid#2654
Eric McIntyre
Eric McIntyre2y ago
"native" Keyword in java means a method is natively executed
Submission from imraklr#1712
Eric McIntyre
Eric McIntyre2y ago
Native keyword is used to declare a method as Native ( method implementation is present in a different language).
Submission from ᴀηωєѕн#7940
Eric McIntyre
Eric McIntyre2y ago
t means that method implementation is present in the different language
Eric McIntyre
Eric McIntyre2y ago
it*
Submission from Error_#1345
Eric McIntyre
Eric McIntyre2y ago
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
Eric McIntyre
Eric McIntyre2y ago
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:
package example;

class JniExample {
public static native void sayHi();
static {
System.loadLibrary("example"); // example.dll on windows, libexample.so on linux
}
public static void main(String[] args) {sayHi();}
}
package example;

class JniExample {
public static native void sayHi();
static {
System.loadLibrary("example"); // example.dll on windows, libexample.so on linux
}
public static void main(String[] args) {sayHi();}
}
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:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class example_JniExample */

#ifndef _Included_example_JniExample
#define _Included_example_JniExample
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: example_JniExample
* Method: sayHi
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_example_JniExample_sayHi
(JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class example_JniExample */

#ifndef _Included_example_JniExample
#define _Included_example_JniExample
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: example_JniExample
* Method: sayHi
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_example_JniExample_sayHi
(JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif
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)
Eric McIntyre
Eric McIntyre2y ago
#include "example_JniExample.h"
#include <iostream>

JNIEXPORT void JNICALL Java_example_JniExample_sayHi
(JNIEnv* env, jclass thisClass) {
std::cout << "Hello chat" << std::endl;
}
#include "example_JniExample.h"
#include <iostream>

JNIEXPORT void JNICALL Java_example_JniExample_sayHi
(JNIEnv* env, jclass thisClass) {
std::cout << "Hello chat" << std::endl;
}
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
Eric McIntyre
Eric McIntyre2y ago
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:
public class NativeDemo{
static{
System.loadLibrary("nativedemo");
}
private native void testMethod();
public static void main(String[] args){
new NativeDemo().testMethod();
}
}
public class NativeDemo{
static{
System.loadLibrary("nativedemo");
}
private native void testMethod();
public static void main(String[] args){
new NativeDemo().testMethod();
}
}
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:
javac -h . NativeDemo.java
javac -h . NativeDemo.java
Then, javac does not just create a NativeDemo.class file but also a NativeDemo.h file as follows:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class NativeDemo */

#ifndef _Included_NativeDemo
#define _Included_NativeDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: NativeDemo
* Method: testMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_NativeDemo_testMethod
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class NativeDemo */

#ifndef _Included_NativeDemo
#define _Included_NativeDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: NativeDemo
* Method: testMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_NativeDemo_testMethod
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
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.
Eric McIntyre
Eric McIntyre2y ago
When the Foreign Function & Memory API is enabled, it is possible to call a native method like this:
Linker linker = Linker.nativeLinker();//1
SymbolLookup stdlib = linker.defaultLookup();//2
MethodHandle strlen = linker.downcallHandle(//3
stdlib.find("strlen").get(),//3.1
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));//3.2
Object len;
try (Arena offHeap = Arena.openConfined()) {//4
MemorySegment cString = offHeap.allocateUtf8String("Hello World");//5
len = strlen.invoke(cString);//6
}
System.out.println(len);//prints 11
Linker linker = Linker.nativeLinker();//1
SymbolLookup stdlib = linker.defaultLookup();//2
MethodHandle strlen = linker.downcallHandle(//3
stdlib.find("strlen").get(),//3.1
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));//3.2
Object len;
try (Arena offHeap = Arena.openConfined()) {//4
MemorySegment cString = offHeap.allocateUtf8String("Hello World");//5
len = strlen.invoke(cString);//6
}
System.out.println(len);//prints 11
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
Want results from more Discord servers?
Add your server