Java Native Access (JNA) provides Java programs easy access to native shared libraries without writing anything but Java code - no JNI or native code is required.
npx @tessl/cli install tessl/maven-net-java-dev-jna--jna@4.5.0Java Native Access (JNA) provides Java programs easy access to native shared libraries without writing anything but Java code - no JNI or native code is required. This functionality is comparable to Windows' Platform/Invoke and Python's ctypes.
// Maven dependency
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.5.2</version>
</dependency>import com.sun.jna.*;
import com.sun.jna.ptr.*;
import com.sun.jna.win32.*;
// Core classes
import com.sun.jna.Native;
import com.sun.jna.Library;
import com.sun.jna.Pointer;
import com.sun.jna.Memory;
import com.sun.jna.Structure;
import com.sun.jna.Function;
import com.sun.jna.NativeLibrary;import com.sun.jna.*;
// Define library interface
public interface CLibrary extends Library {
CLibrary INSTANCE = Native.loadLibrary("c", CLibrary.class);
/**
* Print formatted string to stdout
* @param format Format string
* @param args Arguments for format string
* @return Number of characters written
*/
int printf(String format, Object... args);
/**
* Get length of string
* @param s Null-terminated string
* @return Length of string
*/
int strlen(String s);
}
// Usage
CLibrary.INSTANCE.printf("Hello World\n");
int len = CLibrary.INSTANCE.strlen("Hello");JNA is organized into several key components that work together to provide seamless Java-to-native interoperability:
Load native libraries and call functions with automatic type conversion:
// Load library by name
MyLibrary lib = Native.loadLibrary("mylib", MyLibrary.class);
// Get native library instance for advanced usage
NativeLibrary nativeLib = NativeLibrary.getInstance("mylib");
Function func = nativeLib.getFunction("my_function");
// Direct function invocation
Object result = func.invoke(String.class, new Object[]{"arg1", 42});→ See Memory Management Documentation
Safely allocate and access native memory:
// Allocate native memory
Memory buffer = new Memory(1024);
// Type-safe memory access
buffer.setInt(0, 42);
buffer.setString(4, "Hello", "UTF-8");
int value = buffer.getInt(0);
String text = buffer.getString(4, "UTF-8");
// Pointer arithmetic and sharing
Pointer shared = buffer.share(100, 200); // Offset 100, size 200→ See Memory Management Documentation
Map Java classes to native structures:
public static class Point extends Structure {
public int x, y;
public Point() { super(); }
public Point(int x, int y) {
this.x = x;
this.y = y;
write(); // Write fields to native memory
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("x", "y");
}
}
// Usage
Point p = new Point(10, 20);
someNativeFunction(p); // Pass by reference
p.read(); // Read updated values from native memory→ See Structures & Types Documentation
Handle native functions that modify parameters using ByReference classes:
import com.sun.jna.ptr.*;
// Native function: int divide(int dividend, int divisor, int* quotient, int* remainder)
public interface MathLibrary extends Library {
MathLibrary INSTANCE = Native.loadLibrary("mathlib", MathLibrary.class);
int divide(int dividend, int divisor,
IntByReference quotient, IntByReference remainder);
}
// Usage
IntByReference quotient = new IntByReference();
IntByReference remainder = new IntByReference();
int result = MathLibrary.INSTANCE.divide(17, 5, quotient, remainder);
if (result == 0) {
System.out.println("17 ÷ 5 = " + quotient.getValue() + " remainder " + remainder.getValue());
}
// Available reference types: IntByReference, LongByReference, ByteByReference,
// ShortByReference, FloatByReference, DoubleByReference, PointerByReference, etc.→ See Pointer References Documentation
Windows-specific functionality with stdcall support:
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;
public interface Kernel32 extends StdCallLibrary {
Kernel32 INSTANCE = Native.loadLibrary("kernel32", Kernel32.class,
W32APIOptions.DEFAULT_OPTIONS);
/**
* Get current process ID
* @return Current process ID
*/
int GetCurrentProcessId();
/**
* Get handle to current process
* @return Handle to current process
*/
Pointer GetCurrentProcess();
}→ See Windows Integration Documentation
The Native class provides the core functionality for loading libraries and accessing native operations:
/**
* Load a native library and create a Java interface proxy
* @param name Library name (without extension)
* @param interfaceClass Interface extending Library
* @return Proxy instance implementing the interface
*/
public static <T extends Library> T loadLibrary(String name, Class<T> interfaceClass);
/**
* Load library with custom options
* @param name Library name
* @param interfaceClass Interface class
* @param options Map of library options
* @return Proxy instance
*/
public static <T extends Library> T loadLibrary(String name, Class<T> interfaceClass,
Map<String, ?> options);
/**
* Allocate native memory
* @param size Number of bytes to allocate
* @return Pointer to allocated memory
*/
public static long malloc(long size);
/**
* Free native memory
* @param ptr Pointer returned by malloc
*/
public static void free(long ptr);
// Constants
public static final int POINTER_SIZE; // Size of native pointer (4 or 8 bytes)
public static final int WCHAR_SIZE; // Size of wide character
public static final boolean DEBUG_LOAD; // Debug library loading
public static final boolean DEBUG_JNI; // Debug JNI operations/**
* Base interface for all native library mappings
*/
public interface Library {
// Library options for customization
String OPTION_TYPE_MAPPER = "type-mapper";
String OPTION_FUNCTION_MAPPER = "function-mapper";
String OPTION_INVOCATION_MAPPER = "invocation-mapper";
String OPTION_STRUCTURE_ALIGNMENT = "structure-alignment";
String OPTION_STRING_ENCODING = "string-encoding";
String OPTION_ALLOW_OBJECTS = "allow-objects";
String OPTION_CALLING_CONVENTION = "calling-convention";
String OPTION_CLASSLOADER = "classloader";
String OPTION_OPEN_FLAGS = "open-flags";
}/**
* Platform information and detection utilities
*/
public final class Platform {
// OS Type constants
public static final int MAC = 0;
public static final int LINUX = 1;
public static final int WINDOWS = 2;
public static final int SOLARIS = 3;
public static final int FREEBSD = 4;
public static final int OPENBSD = 5;
public static final int WINDOWSCE = 6;
public static final int AIX = 7;
public static final int ANDROID = 8;
public static final int GNU = 9;
public static final int KFREEBSD = 10;
public static final int NETBSD = 11;
// Current platform info
public static final String ARCH; // Current architecture
public static final String C_LIBRARY_NAME; // Standard C library name
public static final String MATH_LIBRARY_NAME; // Math library name
// Feature detection
public static final boolean HAS_AWT; // AWT support available
public static final boolean HAS_BUFFERS; // NIO Buffer support
public static final boolean HAS_JAWT; // Java AWT Native Interface
public static final boolean HAS_DLL_CALLBACKS; // DLL callback support
/**
* Get current OS type constant
* @return OS type constant (MAC, LINUX, WINDOWS, etc.)
*/
public static int getOSType();
/**
* Check if running on macOS
* @return true if macOS
*/
public static boolean isMac();
/**
* Check if running on Linux
* @return true if Linux
*/
public static boolean isLinux();
/**
* Check if running on Windows
* @return true if Windows
*/
public static boolean isWindows();
/**
* Check if running on Android
* @return true if Android
*/
public static boolean isAndroid();
/**
* Check if 64-bit platform
* @return true if 64-bit
*/
public static boolean is64Bit();
/**
* Check if Intel/x86 architecture
* @return true if Intel architecture
*/
public static boolean isIntel();
/**
* Check if PowerPC architecture
* @return true if PowerPC
*/
public static boolean isPPC();
/**
* Check if ARM architecture
* @return true if ARM
*/
public static boolean isARM();
}/**
* Exception thrown when native function returns error code
*/
public class LastErrorException extends RuntimeException {
/**
* Create exception with message
* @param msg Error message
*/
public LastErrorException(String msg);
/**
* Create exception with error code
* @param code Native error code (errno or GetLastError)
*/
public LastErrorException(int code);
/**
* Get the native error code
* @return Error code from errno or GetLastError
*/
public int getErrorCode();
}// Define interface
public interface MathLibrary extends Library {
MathLibrary INSTANCE = Native.loadLibrary("m", MathLibrary.class);
double sin(double x);
double cos(double x);
double sqrt(double x);
}
// Use library
double result = MathLibrary.INSTANCE.sin(Math.PI / 2);Map<String, Object> options = new HashMap<>();
options.put(Library.OPTION_STRING_ENCODING, "UTF-8");
options.put(Library.OPTION_STRUCTURE_ALIGNMENT, Structure.ALIGN_DEFAULT);
MyLibrary lib = Native.loadLibrary("mylib", MyLibrary.class, options);// Get function pointer from library
NativeLibrary lib = NativeLibrary.getInstance("mylib");
Function func = lib.getFunction("callback_setter");
// Create callback
Callback cb = new Callback() {
public int callback(int value) {
return value * 2;
}
};
// Pass callback to native function
func.invoke(Void.class, new Object[]{cb});The Function class provides direct access to native function pointers with flexible invocation capabilities:
/**
* Represents a pointer to a native function with invocation methods
*/
public class Function extends Pointer {
// Call convention constants
public static final int C_CONVENTION = 0; // Standard C calling convention
public static final int ALT_CONVENTION = 0x3F; // Alternate convention (stdcall)
public static final int THROW_LAST_ERROR = 0x40; // Throw exception on error
public static final int USE_VARARGS = 0x180; // Enable varargs support
public static final int MAX_NARGS = 256; // Maximum argument count
/**
* Get function from library by name (C convention)
* @param libraryName Library containing the function
* @param functionName Name of the function
* @return Function instance
*/
public static Function getFunction(String libraryName, String functionName);
/**
* Get function with specific calling convention
* @param libraryName Library containing the function
* @param functionName Name of the function
* @param callFlags Calling convention and options
* @return Function instance
*/
public static Function getFunction(String libraryName, String functionName, int callFlags);
/**
* Get function with encoding
* @param libraryName Library containing the function
* @param functionName Name of the function
* @param callFlags Calling convention and options
* @param encoding String encoding for parameters
* @return Function instance
*/
public static Function getFunction(String libraryName, String functionName,
int callFlags, String encoding);
/**
* Get function from native pointer address
* @param functionAddress Native address of function
* @return Function instance
*/
public static Function getFunction(Pointer functionAddress);
/**
* Get function from native pointer with options
* @param functionAddress Native address of function
* @param callFlags Calling convention and options
* @return Function instance
*/
public static Function getFunction(Pointer functionAddress, int callFlags);
/**
* Invoke function with return type and arguments
* @param returnType Expected return type
* @param args Function arguments
* @return Result converted to return type
*/
public Object invoke(Class<?> returnType, Object[] args);
/**
* Invoke function with options
* @param returnType Expected return type
* @param args Function arguments
* @param options Invocation options
* @return Result converted to return type
*/
public Object invoke(Class<?> returnType, Object[] args, Map<String, ?> options);
/**
* Invoke using reflection method information
* @param method Method providing signature information
* @param parameterTypes Expected parameter types
* @param returnType Expected return type
* @param args Function arguments
* @param options Invocation options
* @return Result converted to return type
*/
public Object invoke(Method method, Class<?>[] parameterTypes, Class<?> returnType,
Object[] args, Map<String, ?> options);
/**
* Get function name
* @return Function name or address string
*/
public String getName();
/**
* Get calling convention flags
* @return Calling convention and option flags
*/
public int getCallingConvention();
// Type-specific invocation methods
public void invokeVoid(Object[] args);
public int invokeInt(Object[] args);
public long invokeLong(Object[] args);
public float invokeFloat(Object[] args);
public double invokeDouble(Object[] args);
public Pointer invokePointer(Object[] args);
public String invokeString(Object[] args);
}// Direct function access with different return types
Function strlen = Function.getFunction("c", "strlen");
int length = (Integer) strlen.invoke(Integer.class, new Object[]{"Hello"});
Function malloc = Function.getFunction("c", "malloc");
Pointer memory = (Pointer) malloc.invoke(Pointer.class, new Object[]{1024L});
Function free = Function.getFunction("c", "free");
free.invoke(Void.class, new Object[]{memory});
// Windows stdcall function
Function messageBox = Function.getFunction("user32", "MessageBoxA", Function.ALT_CONVENTION);
int result = (Integer) messageBox.invoke(Integer.class,
new Object[]{Pointer.NULL, "Hello World", "Title", 0});
// Function with error checking
Function openFile = Function.getFunction("kernel32", "CreateFileA",
Function.ALT_CONVENTION | Function.THROW_LAST_ERROR);
try {
Pointer handle = (Pointer) openFile.invoke(Pointer.class,
new Object[]{"test.txt", 0x40000000, 0, null, 3, 0, null});
} catch (LastErrorException e) {
System.err.println("Failed to open file: " + e.getMessage());
}// JNA version constants (internal)
String JNA_VERSION = "4.5.2"; // Library version
String NATIVE_VERSION = "5.0.1"; // Native library versionThis documentation provides comprehensive coverage of JNA's API for seamless Java-to-native interoperability without requiring JNI knowledge or native code development.