Definition
Seven operators work on the binary representation of integers:
- & (AND): Result bit is 1 only if both input bits are 1.
- | (OR): Result bit is 1 if either input bit is 1.
- ^ (XOR): Result bit is 1 if the input bits are different.
- ~ (NOT): Flips every bit.
- << (left shift): Shifts bits left, fills with zeros. Equivalent to multiply by 2^n.
- >> (signed right shift): Shifts bits right, preserves the sign bit. Equivalent to divide by 2^n.
- >>> (unsigned right shift): Shifts bits right, fills with zeros. Treats the value as unsigned.
Think of a bank's permission system where each feature is a checkbox in a row of 8 checkboxes (bits). AND checks if a specific checkbox is ticked. OR ticks a new checkbox. XOR toggles a checkbox. NOT inverts all checkboxes. Shift moves the whole row left or right.
Understanding the Concepts
How does each bitwise operator work?
int a = 0b1100; // 12 in binary
int b = 0b1010; // 10 in binary
System.out.println(Integer.toBinaryString(a & b)); // 1000 (AND: both 1 → 1)
System.out.println(Integer.toBinaryString(a | b)); // 1110 (OR: either 1 → 1)
System.out.println(Integer.toBinaryString(a ^ b)); // 0110 (XOR: differ → 1)
System.out.println(Integer.toBinaryString(~a)); // ...11110011 (NOT: flip all)
Truth table:
| A | B | A & B | A \| B | A ^ B | ~A |
|---|---|-------|--------|-------|----|
| 0 | 0 | 0 | 0 | 0 | 1 |
| 0 | 1 | 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 | 0 |
| 1 | 1 | 1 | 1 | 0 | 0 |
How are bitwise operators used for permission flags?
This is the most common real-world use in banking systems:
// Define permission bits
final int VIEW_BALANCE = 0b0001; // bit 0
final int TRANSFER = 0b0010; // bit 1
final int INTERNATIONAL = 0b0100; // bit 2
final int ADMIN = 0b1000; // bit 3
// Rajesh has view + transfer permissions
int rajeshPerms = VIEW_BALANCE | TRANSFER; // 0b0011 = 3
// Check if Rajesh can transfer
boolean canTransfer = (rajeshPerms & TRANSFER) != 0; // true
// Add international permission
rajeshPerms = rajeshPerms | INTERNATIONAL; // 0b0111 = 7
// Remove transfer permission
rajeshPerms = rajeshPerms & ~TRANSFER; // 0b0101 = 5
// Toggle admin permission
rajeshPerms = rajeshPerms ^ ADMIN; // 0b1101 = 13
How do shift operators work?
// Left shift: multiply by 2^n
int amount = 100;
System.out.println(amount << 1); // 200 (100 * 2)
System.out.println(amount << 3); // 800 (100 * 8)
// Signed right shift: divide by 2^n, preserves sign
System.out.println(amount >> 1); // 50 (100 / 2)
System.out.println(-100 >> 1); // -50 (sign preserved)
// Unsigned right shift: fills with 0, ignores sign
System.out.println(-1 >>> 1); // 2147483647 (Integer.MAX_VALUE)
System.out.println(-1 >> 1); // -1 (sign preserved)
Why does >>> matter?
Java has no unsigned integer type. When you need to treat an int's bits as unsigned (e.g., in hash functions or protocol parsing), >>> is essential.
// HashMap uses >>> to spread hash bits
static int hash(Object key) {
int h = key.hashCode();
return h ^ (h >>> 16); // spread high bits into low bits
}
// Without >>>, negative hash codes would stay negative
// With >>>, the sign bit is treated as a data bit
int h = -12345;
System.out.println(h >> 16); // -1 (sign extended)
System.out.println(h >>> 16); // 65535 (zero-filled, unsigned)
What are useful bit tricks?
// Check if a number is a power of 2
boolean isPow2 = (n > 0) && (n & (n - 1)) == 0;
// 8 = 1000, 7 = 0111, 1000 & 0111 = 0000 → true
// Fast modulo for power-of-2 divisor
int index = hashCode & (arrayLength - 1); // same as hashCode % arrayLength
// HashMap uses this for bucket index
// Swap two variables without a temp
int x = 5, y = 10;
x = x ^ y; // x = 15
y = x ^ y; // y = 5
x = x ^ y; // x = 10
What are common mistakes?
- Confusing & with &&.
&is bitwise,&&is logical short-circuit. Using&in a null guard (obj != null & obj.method()) crashes. - Shifting by 32 or more.
1 << 32is 1, not 0, because Java usesshift % 32for int. Use long for shifts of 32+. - Forgetting ~ includes the sign bit.
~0is -1 (all bits set in two's complement), not some large positive number. - Using >> instead of >>> for unsigned operations. >> preserves the sign, >>> fills with zeros. Hash spreading needs >>>.
What should a senior engineer know?
- EnumSet is backed by a bitmask. For enums with up to 64 values, EnumSet uses a single long with bitwise operations. Very fast.
- Bitwise operations are used in serialization formats. Protocol Buffers, Kafka wire protocol, and JDBC result set flags all use bit packing.
- Integer.bitCount(), highestOneBit(), numberOfLeadingZeros() are hardware-accelerated on most JVMs (intrinsics).
- Bit manipulation in financial systems is used for compact feature flags, account type encoding, and permission matrices in RBAC systems.
Visual Overview
Bitwise operators:
& AND: 1100 & 1010 = 1000
| OR: 1100 | 1010 = 1110
^ XOR: 1100 ^ 1010 = 0110
~ NOT: ~1100 = ...0011
Shift operators:
100 << 1 = 200 (multiply by 2)
100 >> 1 = 50 (divide by 2, signed)
-1 >>> 1 = MAX (unsigned, fill with 0)
-1 >> 1 = -1 (signed, fill with 1)
Permission flags:
VIEW = 0001 check: (perms & VIEW) != 0
TRANSFER= 0010 add: perms | TRANSFER
INTL = 0100 remove: perms & ~TRANSFER
ADMIN = 1000 toggle: perms ^ ADMIN
Key Takeaway
Bitwise operators work on individual bits of integers. AND (&) checks if specific bits are set. OR (|) sets bits. XOR (^) toggles bits. NOT (~) flips all bits. Left shift (<<) multiplies by powers of 2. Signed right shift (>>) divides by powers of 2 and preserves the sign. Unsigned right shift (>>>) fills with zeros and treats the value as unsigned.
In banking systems, the most common use is permission flags: each feature is a bit, and you use & to check, | to grant, and & ~mask to revoke. HashMap uses ^ and >>> to spread hash bits for better distribution. And the power-of-2 modulo trick index & (length - 1) is how HashMap computes bucket indices without division.
One sentence to remember: & checks bits, | sets bits, ^ toggles bits, << multiplies by 2, >> divides with sign, >>> divides without sign.
---