Low level programming with Java
Java hides a surprisingly capable “low-level subset” for doing bit-twiddly, memory-adjacent work while still staying safe(-ish). Here’s a compact map with the key tools, what they’re for, and tiny examples.
1) Bitwise core: operators & integer promotions
-
Types:
byte
(8, signed),short
(16, signed),char
(16, unsigned),int
(32, signed),long
(64, signed). -
Operators:
& | ^ ~ << >> >>>
-
>>
= arithmetic right shift (sign-extends) -
>>>
= logical right shift (zero-fills)
-
-
Small types (
byte/short/char
) promote toint
before ops—mask when you need exact widths.
2) Unsigned helpers (on signed primitives)
-
Convert/compare/divide as if unsigned:
-
Byte.toUnsignedInt(byte b)
,Short.toUnsignedInt(short s)
-
Integer.toUnsignedLong(int x)
,Integer.compareUnsigned
,Integer.divideUnsigned
,Integer.remainderUnsigned
-
Same for
Long
(compare/divide/remainder as unsigned)
-
3) Integer/Long bit utilities (fast, often intrinsic)
Integer
and Long
expose high-quality bit hacks:
-
bitCount(x)
— popcount -
numberOfLeadingZeros(x)
/numberOfTrailingZeros(x)
-
rotateLeft(x, n)
/rotateRight(x, n)
-
reverse(x)
— bit reverse -
reverseBytes(x)
— endianness swap -
highestOneBit(x)
/lowestOneBit(x)
4) Float/Double bit-level access (IEEE-754)
Exact reinterprets without changing payloads (incl. signaling NaNs):
-
Float.floatToRawIntBits(float f)
-
Float.intBitsToFloat(int bits)
-
Double.doubleToRawLongBits(double d)
-
Double.longBitsToDouble(long bits)
Other IEEE helpers:
-
Float/Double.isFinite
,isNaN
,isInfinite
-
nextUp/nextDown
,ulp
,getExponent
,scalb
,copySign
-
Math.fma
,StrictMath
(strict IEEE semantics),strictfp
(class/method modifier)
5) Byte order and packing
-
ByteBuffer
+order(ByteOrder.LITTLE_ENDIAN)
for explicit endianness. -
Integer.reverseBytes
/Long.reverseBytes
for quick swaps.
6) Raw-ish memory without Unsafe
: NIO & FFM
NIO Direct/Memory-mapped buffers
-
FileChannel.map(...)
→MappedByteBuffer
(off-heap, OS-backed). -
ByteBuffer.allocateDirect(n)
for off-heap scratch. -
Great for struct-like layouts with manual indexing.
Foreign Function & Memory (Project Panama)
-
MemorySegment
,MemoryLayout
,VarHandle
-based layout accessors (noUnsafe
, bounds-checked, off-heap). -
Lets you define C-style structs and access fields at addresses. (Availability depends on your JDK version; on recent JDKs it’s standard, earlier it was preview/incubator.)
7) Concurrency-level primitives (low-level flavor)
Atomics & fences
-
java.util.concurrent.atomic.*
(AtomicInteger
,AtomicLong
,AtomicReferenceArray
, etc.) -
Field updaters:
AtomicIntegerFieldUpdater
for CAS on volatile fields. -
VarHandle
(since Java 9): fine-grained memory semantics:getOpaque
,getAcquire
,setRelease
,compareAndSet
,getAndAdd
, etc.
8) Fast array/memory moves & diffs
-
System.arraycopy(src, si, dst, di, len)
— usually a native bulk copy. -
Arrays.mismatch(a, b)
— first differing index (-1 if equal). -
Arrays.hashCode(...)
on primitives is vectorized on some JVMs.
9) Big bitsets/bignums
-
BitSet
— compact bit arrays with logical ops (and
,or
,xor
,andNot
,nextSetBit
). -
BigInteger
— arbitrary precision with low-level operations, plustestBit
,setBit
,clearBit
,flipBit
,bitLength
,bitCount
,shiftLeft/Right
.
10) Checked overflow & exact math
-
Math.addExact/subtractExact/multiplyExact
,incrementExact/negateExact
-
Math.floorDiv/floorMod
(mathematically consistent with negatives)
11) CRCs and light hashing over bytes
-
java.util.zip.CRC32
,Adler32
for streaming checksums (simple, fast) -
MessageDigest
for stronger hashes (SHA-family) when needed.
12) Performance/intrinsics odds & ends
-
Many of the above map to HotSpot intrinsics (very fast), e.g.
bitCount
, rotates,arraycopy
,fma
. -
@Contended
(JEP 142) can reduce false-sharing on fields (needs-XX:-RestrictContended
). -
final
,volatile
, escape-analysis friendly patterns help the JIT.
13) Practical float/int reinterpretation patterns
Extract IEEE-754 fields
Create a quiet NaN with custom payload
Encode/decode a 24-bit unsigned integer in 3 bytes
14) Things that feel low-level but are better avoided
-
sun.misc.Unsafe
(non-portable, access restricted). PreferVarHandle
,ByteBuffer
, orMemorySegment
. -
Hand-rolled pointer arithmetic: Java doesn’t expose raw pointers; emulate with indices and bounds checks (or FFM API for off-heap with safety).
Quick “what to use when” cheat sheet
-
Bit hacks/popcount/rotates:
Integer/Long
bit utilities -
Unsigned math:
Byte/Short/Integer/Long
unsigned methods -
Endian swaps:
reverseBytes
,ByteBuffer.order
-
Float–int reinterprets:
floatToRawIntBits
/intBitsToFloat
(andDouble
variants) -
Struct-like I/O:
ByteBuffer
(heap/direct) orMappedByteBuffer
-
Off-heap structured access / native interop:
MemorySegment
(+ layouts/VarHandles) -
Atomic CAS/fences:
VarHandle
oratomic.*
-
Bulk copy/diff:
System.arraycopy
,Arrays.mismatch
-
Bit arrays:
BitSet
; big math:BigInteger
Comments
Post a Comment