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.
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: 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`."
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
- 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.