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.
—
This document covers JNA's ptr package, which provides by-reference parameter passing functionality essential for interacting with native functions that modify parameters.
import com.sun.jna.ptr.*;
// Specific reference types
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.LongByReference;
import com.sun.jna.ptr.ByteByReference;
import com.sun.jna.ptr.ShortByReference;
import com.sun.jna.ptr.FloatByReference;
import com.sun.jna.ptr.DoubleByReference;
import com.sun.jna.ptr.PointerByReference;
import com.sun.jna.ptr.NativeLongByReference;The ByReference abstract class provides the foundation for pass-by-reference parameter types:
/**
* Base class for pointer-to-type functionality used in C code
* to return values to the caller in addition to function results
*/
public abstract class ByReference extends PointerType {
/**
* Constructor allocating memory for the data type
* @param dataSize Size in bytes of the data type
*/
protected ByReference(int dataSize);
}/**
* Provides pass-by-reference for int values
*/
public class IntByReference extends ByReference {
/**
* Create with zero value
*/
public IntByReference();
/**
* Create with specific value
* @param value Initial int value
*/
public IntByReference(int value);
/**
* Set the int value
* @param value New int value
*/
public void setValue(int value);
/**
* Get the int value
* @return Current int value
*/
public int getValue();
}/**
* Provides pass-by-reference for long values
*/
public class LongByReference extends ByReference {
/**
* Create with zero value
*/
public LongByReference();
/**
* Create with specific value
* @param value Initial long value
*/
public LongByReference(long value);
/**
* Set the long value
* @param value New long value
*/
public void setValue(long value);
/**
* Get the long value
* @return Current long value
*/
public long getValue();
}/**
* Provides pass-by-reference for byte values
*/
public class ByteByReference extends ByReference {
/**
* Create with zero value
*/
public ByteByReference();
/**
* Create with specific value
* @param value Initial byte value
*/
public ByteByReference(byte value);
/**
* Set the byte value
* @param value New byte value
*/
public void setValue(byte value);
/**
* Get the byte value
* @return Current byte value
*/
public byte getValue();
}/**
* Provides pass-by-reference for short values
*/
public class ShortByReference extends ByReference {
/**
* Create with zero value
*/
public ShortByReference();
/**
* Create with specific value
* @param value Initial short value
*/
public ShortByReference(short value);
/**
* Set the short value
* @param value New short value
*/
public void setValue(short value);
/**
* Get the short value
* @return Current short value
*/
public short getValue();
}/**
* Provides pass-by-reference for float values
*/
public class FloatByReference extends ByReference {
/**
* Create with zero value
*/
public FloatByReference();
/**
* Create with specific value
* @param value Initial float value
*/
public FloatByReference(float value);
/**
* Set the float value
* @param value New float value
*/
public void setValue(float value);
/**
* Get the float value
* @return Current float value
*/
public float getValue();
}/**
* Provides pass-by-reference for double values
*/
public class DoubleByReference extends ByReference {
/**
* Create with zero value
*/
public DoubleByReference();
/**
* Create with specific value
* @param value Initial double value
*/
public DoubleByReference(double value);
/**
* Set the double value
* @param value New double value
*/
public void setValue(double value);
/**
* Get the double value
* @return Current double value
*/
public double getValue();
}/**
* Provides pass-by-reference for Pointer values (void** in C)
*/
public class PointerByReference extends ByReference {
/**
* Create with null pointer
*/
public PointerByReference();
/**
* Create with specific pointer
* @param value Initial pointer value
*/
public PointerByReference(Pointer value);
/**
* Set the pointer value
* @param value New pointer value
*/
public void setValue(Pointer value);
/**
* Get the pointer value
* @return Current pointer value
*/
public Pointer getValue();
}/**
* Provides pass-by-reference for NativeLong values
* (platform-specific long type)
*/
public class NativeLongByReference extends ByReference {
/**
* Create with zero value
*/
public NativeLongByReference();
/**
* Create with specific value
* @param value Initial NativeLong value
*/
public NativeLongByReference(NativeLong value);
/**
* Set the NativeLong value
* @param value New NativeLong value
*/
public void setValue(NativeLong value);
/**
* Get the NativeLong value
* @return Current NativeLong value
*/
public NativeLong getValue();
}// Native function that modifies parameters:
// int divide(int dividend, int divisor, int* quotient, int* remainder)
public interface MathLibrary extends Library {
MathLibrary INSTANCE = Native.loadLibrary("mathlib", MathLibrary.class);
/**
* Divide two numbers, returning quotient and remainder by reference
* @param dividend Number to divide
* @param divisor Number to divide by
* @param quotient Output parameter for quotient
* @param remainder Output parameter for remainder
* @return 0 on success, error code on failure
*/
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("Quotient: " + quotient.getValue()); // 3
System.out.println("Remainder: " + remainder.getValue()); // 2
}// Native function: void* malloc(size_t size)
// Native function: void free(void* ptr)
public interface CLibrary extends Library {
CLibrary INSTANCE = Native.loadLibrary("c", CLibrary.class);
Pointer malloc(long size);
void free(Pointer ptr);
// Function that allocates memory and returns pointer via parameter
int allocateBuffer(long size, PointerByReference buffer);
}
// Usage
PointerByReference bufferRef = new PointerByReference();
int result = CLibrary.INSTANCE.allocateBuffer(1024, bufferRef);
if (result == 0) {
Pointer buffer = bufferRef.getValue();
// Use the allocated buffer
buffer.setInt(0, 42);
// Clean up
CLibrary.INSTANCE.free(buffer);
}// Windows API functions using by-reference parameters
public interface Kernel32 extends StdCallLibrary {
Kernel32 INSTANCE = Native.loadLibrary("kernel32", Kernel32.class);
/**
* Get system time
* @param systemTime Structure to receive time
*/
void GetSystemTime(SystemTimeStructure systemTime);
/**
* Get module handle by name
* @param moduleName Name of module (null for current process)
* @param moduleHandle Output parameter for module handle
* @return true on success
*/
boolean GetModuleHandleEx(int flags, String moduleName,
PointerByReference moduleHandle);
}
// Usage
PointerByReference moduleHandle = new PointerByReference();
boolean success = Kernel32.INSTANCE.GetModuleHandleEx(0, null, moduleHandle);
if (success) {
Pointer handle = moduleHandle.getValue();
System.out.println("Module handle: " + handle);
}// Function with multiple output parameters
public interface GeometryLibrary extends Library {
GeometryLibrary INSTANCE = Native.loadLibrary("geometry", GeometryLibrary.class);
/**
* Calculate circle properties
* @param radius Input radius
* @param area Output parameter for area
* @param circumference Output parameter for circumference
* @param diameter Output parameter for diameter
*/
void calculateCircle(double radius,
DoubleByReference area,
DoubleByReference circumference,
DoubleByReference diameter);
}
// Usage
DoubleByReference area = new DoubleByReference();
DoubleByReference circumference = new DoubleByReference();
DoubleByReference diameter = new DoubleByReference();
GeometryLibrary.INSTANCE.calculateCircle(5.0, area, circumference, diameter);
System.out.println("Area: " + area.getValue());
System.out.println("Circumference: " + circumference.getValue());
System.out.println("Diameter: " + diameter.getValue());// Always initialize reference values when needed
IntByReference counter = new IntByReference(0); // Start with known value
// Check return values when functions can fail
PointerByReference ptr = new PointerByReference();
int result = someFunction(ptr);
if (result != 0) {
// Handle error - ptr.getValue() may be invalid
return;
}
// Use the returned pointer
Pointer validPtr = ptr.getValue();// ByReference objects are not thread-safe
// Create separate instances for concurrent access
public class ThreadSafeCounter {
public int increment(int value) {
IntByReference ref = new IntByReference(value);
nativeIncrementFunction(ref);
return ref.getValue();
}
}The ptr package is essential for interacting with native functions that modify parameters or return multiple values, providing type-safe pass-by-reference semantics that match C/C++ pointer parameters.
Install with Tessl CLI
npx tessl i tessl/maven-net-java-dev-jna--jna