Bitwise Operators in Java
Master Java bitwise operators: AND, OR, XOR, NOT, and shift operators for manipulating individual bits, flags, and masks in low-level programming.
Bitwise Operators in Java
Bitwise operators manipulate individual bits in integer values. They are essential for low-level programming, flag management, cryptography, and performance-critical operations.
Introduction
Bitwise operators manipulate individual bits within integer values — & (AND), | (OR), ^ (XOR), ~ (NOT), << (left shift), >> (signed right shift), and >>> (unsigned right shift). They are the operators of choice for low-level programming: flag management, cryptographic operations, network protocol implementation, and performance-critical numeric algorithms. Most application-level Java code rarely touches these operators, which is precisely why when you do need them — working with file permissions, encoding/decoding binary protocols, implementing hash functions — the knowledge gap can be costly.
The most dangerous gotcha is sign extension with the signed right shift >> on negative numbers. When you shift -4 >> 1, the result is -2 (the sign bit is replicated as bits shift right). But -4 >>> 1 yields 1073741823 — an entirely different number because >>> fills with zeros regardless of the sign bit. Using >> where >>> was intended on bit patterns that represent unsigned quantities produces silently incorrect results that may not surface until the bug reaches production. Another common mistake: ^ is XOR, not exponentiation. 2 ^ 3 evaluates to 1, not 8. If you need Math.pow(2, 3), you must call it explicitly.
Bit flags are the most practical application. Using power-of-2 values as individual flag bits within a single integer — FLAG_READ = 1 << 0, FLAG_WRITE = 1 << 1, FLAG_EXECUTE = 1 << 2 — allows combining multiple flags via OR and checking individual flags via AND. Unix file permissions (rwxr-xr-x) are a canonical example: permissions & FLAG_READ checks if the read bit is set. This post covers all operators in detail, the flag and mask pattern for combining permissions, the failure scenarios from sign extension to XOR swap edge cases, and the security considerations for bit-level permission checks and constant-time cryptographic operations.
When to Use / Not to Use
Use bitwise operators when:
- Working with flags and bit masks (e.g., file permissions, configuration)
- Performing cryptographic operations
- Implementing efficient data structures (sets, compression)
- Writing high-performance numeric algorithms
- Encoding/decoding data at the bit level
Do not use bitwise operators when:
- Working with non-integer types without explicit conversion
- Performing arithmetic where semantic meaning matters
- Dealing with signed negative numbers (behavior can be surprising with
>>>) - Code readability is more important than micro-optimization
Diagram: Bitwise Operations at the Bit Level
graph LR
A["1010"] --> B["& AND"]
C["1100"] --> B
B --> D["1000"]
E["1010"] --> F["| OR"]
C --> F
F --> G["1110"]
H["1010"] --> I["^ XOR"]
C --> I
I --> J["0110"]
K["1010"] --> L["~ NOT"]
L --> M["0101"]
N["0101 << 1"] --> O["1010"]
P["1010 >> 1"] --> Q["0101"]
Code Snippet: Flags and Masks Pattern
public class BitwiseDemo {
// Define flags as powers of 2 for bit mask operations
public static final int FLAG_READ = 1 << 0; // 0001
public static final int FLAG_WRITE = 1 << 1; // 0010
public static final int FLAG_EXECUTE = 1 << 2; // 0100
public static final int FLAG_ADMIN = 1 << 3; // 1000
public static void main(String[] args) {
int permissions = FLAG_READ | FLAG_WRITE; // 0011
// Check if a flag is set
boolean canRead = (permissions & FLAG_READ) != 0; // true
boolean canExecute = (permissions & FLAG_EXECUTE) != 0; // false
// Add a flag
permissions |= FLAG_EXECUTE; // 0111
// Remove a flag
permissions &= ~FLAG_EXECUTE; // 0011
// Toggle a flag
permissions ^= FLAG_EXECUTE; // 0111 -> 0011
permissions ^= FLAG_EXECUTE; // 0011 -> 0111
// Signed right shift vs unsigned
int negative = -4; // Binary: 11111111111111111111111111111100
System.out.println(">> 2: " + (negative >> 2)); // -1 (sign-extended)
System.out.println(">>> 2: " + (negative >>> 2)); // 1073741823 (zero-filled)
// Common use: checking even/odd
int number = 42;
boolean isEven = (number & 1) == 0; // Faster than % operation
// Swapping values without temp variable
int a = 5, b = 9;
a ^= b;
b ^= a;
a ^= b; // a = 9, b = 5
}
}
Failure Scenarios
| Scenario | Problem | Solution |
|---|---|---|
Using >> on negative numbers | Sign extension produces unexpected results | Use >>> for unsigned right shift |
Confusing ^ with exponentiation | ^ is XOR, not power operator | Use Math.pow() for exponentiation |
| Bitmask with non-power-of-2 | Overlapping bits cause incorrect results | Always use distinct powers of 2 |
| Negative bit shift amounts | Shift amount must be non-negative | Validate shift amount before operation |
Trade-off Table
| Operator | Description | Use Case |
|---|---|---|
& | Bitwise AND | Mask specific bits |
| | Bitwise OR | Set bits |
^ | Bitwise XOR | Toggle bits, swap without temp |
~ | Bitwise NOT | Create bit masks (flip all bits) |
<< | Left shift | Multiply by 2^n efficiently |
>> | Signed right shift | Divide by 2^n (floor for negative) |
>>> | Unsigned right shift | Divide by 2^n (logical shift) |
Observability Checklist
- Log flag state changes for permission-related operations
- Add assertions when checking flag combinations
- Instrument bit manipulation for cryptographic operations
- Monitor for unexpected signed shift behavior in negative number paths
- Add unit tests for bitmask operations covering edge cases
Security Notes
- Bit-level permission checks: Ensure bitmask comparisons are not vulnerable to TOCTOU (time-of-check-time-of-use) race conditions
- Cryptographic key handling: XOR operations on keys should be constant-time to prevent side-channel attacks
- Flag manipulation in access control: Validate that flag constants are never reused or overlapping
Pitfalls
- Sign extension with
>>:-4 >> 1returns-2, not2. Use>>>for logical shift. ^is XOR, not exponentiation:2 ^ 3returns1, not8. UseMath.pow().- Negative shift amounts: Shift amount must be non-negative.
-1 << 2is valid;-1 << -2throwsNegativeArraySizeExceptionin some JVM versions. - Bitwise ops on negative numbers: Results can be counterintuitive. Document assumptions.
- Width of operations: All bitwise operations work on 32-bit
intor 64-bitlong. Smaller types are promoted.
Quick Recap
- Bitwise
&,|,^operate on individual bits <<and>>shift bits left/right with sign extension>>>shifts right with zero fill (unsigned)~flips all bits- Use power-of-2 values for flags to prevent overlap
- Always use
>>>when dealing with bit patterns that should be treated as unsigned
Interview Questions
Model Answer: "`>>` is signed right shift— it preserves the sign bit (sign extension). `-4 >> 1 = -2`. `>>>` is unsigned right shift— it always fills with zero. `-4 >>> 1 = 1073741823`. Use `>>>` when working with bit patterns that should not be interpreted as negative numbers."
Model Answer: "Use the AND operator with a bitmask. For example, to check bit 3: `(value & (1 << 3)) != 0`. The `1 << 3` creates a mask with bit 3 set. If both bits are 1, the result is non-zero."
Model Answer: "XOR swap uses `a ^= b; b ^= a; a ^= b;` to swap values without a temporary variable. It fails when `a` and `b` reference the same memory location because `x ^= x` sets the value to 0, not preserves it. Modern compilers optimize temp-variable swaps anyway."
Model Answer: "A bitmask is a number with specific bits set used to extract, set, or toggle bits in another number. Common uses: storing multiple boolean flags in a single integer (like Unix file permissions `rwx`), checking multiple states simultaneously, and implementing sets efficiently."
Model Answer: "`~5` evaluates to `-6`. Bitwise NOT flips all bits. `5` in 32-bit binary is `00000000000000000000000000000101`. Flipping gives `11111111111111111111111111111010`, which is `-6` in two's complement representation."
Model Answer: "`1 << 3` equals `8`. Left shift shifts bits left by the specified amount, filling with zeros on the right. Each left shift effectively multiplies by 2. `1 << n` is equivalent to `2^n`."
Model Answer: "In binary, the least significant bit of an even number is `0` and odd number is `1`. `number & 1` masks only that bit. If the result is `0`, the number is even; if `1`, it's odd. This is faster than `number % 2` because it's a single bitwise AND instead of division."
Model Answer: "`^` in Java is the bitwise XOR operator, NOT exponentiation. `2 ^ 3` returns `1` (binary `10` XOR `11 = 01`). For exponentiation, use `Math.pow(2, 3)` which returns `8.0`. Confusing these is a common bug."
Model Answer: "Java masks the shift amount with `31` for int (or `63` for long): `x << 32` is equivalent to `x << 0`, which means no shift occurs. This is defined behavior—the JVM internally uses `shiftAmount & 0x1F` for int."
Model Answer: "Use bitwise OR with a mask that has the target bit set: `value | (1 << bitPosition)`. For example, to set bit 3: `value | 0b1000`. This leaves all other bits unchanged while forcing the target bit to `1`."
Model Answer: "Use bitwise AND with a mask that has the target bit cleared: `value & ~(1 << bitPosition)`. For example, to clear bit 3: `value & ~0b1000`. The `~` flips the mask so only the target bit is `0`, all others are `1` (preserving them through AND)."
Model Answer: "Signed right shift (`>>`) replicates the sign bit on the left side. For `-8 >> 1`, the sign bit (1) is replicated as bits shift right: `-8 >> 1 = -4`. The result maintains the negative sign. Unsigned shift `>>>` always fills with zeros, regardless of sign."
Model Answer: "`5 << 3` equals `40`. Left shift moves bits left by 3 positions, filling with zeros on the right. `5 << 3` is equivalent to `5 * 2^3 = 5 * 8 = 40`. If the shifted result exceeds the type's bit width, overflow wraps according to the type's range."
Model Answer: "XOR (exclusive OR) returns `1` only when bits differ: `1 ^ 0 = 1`, `1 ^ 1 = 0`, `0 ^ 0 = 0`. OR (inclusive OR) returns `1` when at least one bit is `1`: `1 | 0 = 1`, `1 | 1 = 1`, `0 | 0 = 0`. In binary: `1010 ^ 1100 = 0110`, but `1010 | 1100 = 1110`."
Model Answer: iThe bitwise complement (`~`) flips all bits of the operand. `~5` on a 32-bit integer (binary `00000000000000000000000000000101`) produces `-6` (binary `11111111111111111111111111111010`). This is the two's complement representation of `-6`."
Model Answer: "`-1 >>> 1` equals `2147483647` (Integer.MAX_VALUE). `-1` in 32-bit two's complement is all 1s (`11111111111111111111111111111111`). Unsigned right shift fills with zeros, producing `01111111111111111111111111111111`, which is `2147483647`."
Model Answer: "Use XOR with a bitmask where only the target bit is set: `value ^ (1 << bitPosition)`. For example, to toggle bit 3: `value ^ 0b1000`. If the bit was 1, it becomes 0; if it was 0, it becomes 1. Toggling the same bit twice restores the original value."
Model Answer: "`1 << 31` is an `int` expression that overflows because `1` is `int`, and `1 << 31` produces `-2147483648` (Integer.MIN_VALUE, a negative number). `(1L << 31)` is a `long` expression producing `2147483648L` (positive). For bit patterns involving the high bit, use `long` to avoid signed int overflow."
Model Answer: "Use `Integer.bitCount(n)` or `Long.bitCount(n)` for fast bit counting. For example, `Integer.bitCount(0b10110011)` returns `5` (the number of 1 bits). This uses hardware intrinsics where available and is much faster than manual loops."
Model Answer: "For negative numbers, `>>` performs sign extension (fills with 1s), preserving the negative sign. `>>>` always fills with zeros (logical shift). For `-4 >> 1` you get `-2`; for `-4 >>> 1` you get `1073741823`. Use `>>>` when treating the bits as an unsigned value."
Further Reading
- Arithmetic Operators in Java - Numeric operations that complement bitwise manipulation
- Java Enums and Flag Patterns - Using enums to define type-safe bit flags
- Security Manager - Where bitwise operators are essential (XOR encryption, key mangling)
- Network Protocol Implementation - Bit-level operations for protocol parsing
Conclusion
Bitwise operators give you fine-grained control over individual bits, making them indispensable for flag-based permissions systems, network protocol implementations, and performance-critical numeric algorithms. The key insight is treating flags as power-of-2 values to prevent overlapping bits and using XOR for efficient swapping.
Watch out for sign extension with the signed right shift >> on negative numbers—use >>> for logical shifts where the sign bit should not be preserved. Remember that ^ is XOR, not exponentiation; if you need 2^3 = 8, use Math.pow().
These operators complement arithmetic operators at the bit level and are often used alongside break and continue with labels in low-level control flow scenarios. For most application-level code, the readability cost rarely justifies the performance gain—but in embedded systems, cryptography, and compression, these operators are essential.
Category
Related Posts
Abstract Classes in Java
Learn about partially implemented classes that define contracts for subclasses using abstract methods and concrete implementations.
Arithmetic Operators in Java
Master Java arithmetic operators: addition, subtraction, multiplication, division, and modulo with integer division gotchas and operator precedence explained.
Array Basics in Java
Learn Java array fundamentals: declaration, initialization, element access, and the length property explained simply.