Definition
Two families of AND/OR:
- && and || (short-circuit logical).
&&skips the right operand if the left is false (because false AND anything is false).||skips the right if the left is true (because true OR anything is true). This prevents unnecessary evaluation and protects null guards. - & and | (non-short-circuit / bitwise). On boolean operands, they compute the same result but always evaluate both sides. On integer operands, they perform bitwise operations on each pair of bits.
A loan officer checks two conditions: is the account active AND is the balance sufficient? With &&, if the account is not active, she stops immediately (short-circuit) – no need to check the balance. With &, she checks both conditions even if the first already failed. If checking the balance requires opening a locked vault (expensive operation) or the account is null (crash), short-circuiting saves time and prevents errors.
Understanding the Concepts
What is short-circuit evaluation?
Short-circuit means the second operand is not evaluated if the first operand already determines the result.
// && short-circuits: if left is false, right is NEVER evaluated
boolean result = false && expensiveCheck(); // expensiveCheck() NOT called
// || short-circuits: if left is true, right is NEVER evaluated
boolean result2 = true || expensiveCheck(); // expensiveCheck() NOT called
This is important for performance (skip expensive operations) and safety (skip operations that would crash).
Why does short-circuiting matter for null guards?
The most common use of short-circuiting is null-safe access:
Account account = findAccount("SAV2001"); // might return null
// SAFE with &&: if account is null, isActive() is never called
if (account != null && account.isActive()) {
System.out.println("Account is active");
}
// CRASH with &: both sides always evaluated, even if account is null
// if (account != null & account.isActive()) { // NPE if account is null!
With &&, the null check on the left protects the method call on the right. With &, both sides are always evaluated, so account.isActive() is called on null and throws NullPointerException.
How does & work on integers (bitwise AND)?
When used with integer operands, & performs bitwise AND – each bit in the result is 1 only if both corresponding input bits are 1.
int permissions = 0b1010; // binary: has read (bit 3) and execute (bit 1)
int readMask = 0b1000; // binary: read bit
boolean canRead = (permissions & readMask) != 0; // true: bit 3 is set
System.out.println(canRead); // true
// In banking: checking feature flags
int accountFlags = 0b0111; // overdraft + international + premium
int overdraftBit = 0b0001;
boolean hasOverdraft = (accountFlags & overdraftBit) != 0; // true
How does | work on integers (bitwise OR)?
// Combining flags with bitwise OR
int flags = 0;
flags = flags | 0b0001; // set overdraft bit
flags = flags | 0b0100; // set premium bit
System.out.println(Integer.toBinaryString(flags)); // 101
What about ^ (XOR) and ~ (NOT)?
// XOR: bits differ → 1, same → 0
int a = 0b1100;
int b = 0b1010;
System.out.println(Integer.toBinaryString(a ^ b)); // 0110
// NOT: flips all bits
int c = 0b00001111;
System.out.println(Integer.toBinaryString(~c)); // 11111111111111111111111111110000
What about shift operators?
// 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)
// Right shift (signed): divide by 2^n, preserves sign
int negative = -100;
System.out.println(negative >> 1); // -50
// Unsigned right shift: fills with 0, ignores sign
System.out.println(negative >>> 1); // 2147483598 (large positive)
When should you use & vs &&?
| Use case | Operator | Why |
|---|---|---|
| Boolean logic (if conditions) | && / || | Short-circuit protects null guards, saves work |
| Bitwise operations on integers | & / | / ^ / ~ | Bit manipulation, flag checks |
| Force evaluation of both sides (rare) | & / | on booleans | Only if both sides have required side effects |
What are common mistakes?
- Using & instead of && for null guards.
account != null & account.isActive()crashes on null. Always use&&. - Using && for bitwise operations.
flags && maskdoes not compile if flags is int. Use&. - Side effects in short-circuited expressions.
if (true || counter++)never increments counter. Do not put side effects in boolean expressions. - Confusing >> with >>>. Signed right shift preserves the sign bit. Unsigned right shift fills with zeros. Use >>> for hash code calculations.
What should a senior engineer know?
- Bitwise operations are used in permission systems. Unix file permissions, feature flags, and database bit columns all use & and | for efficient multi-flag storage and checking.
- EnumSet uses bitwise operations internally.
EnumSet.of(READ, WRITE)is backed by a long bitmask for efficiency. - Shift operators are used in hash functions.
HashMap.hash()usesh ^ (h >>> 16)to spread hash bits. - Bitwise AND with mask for modulo power-of-2.
index & (length - 1)is equivalent toindex % lengthwhen length is a power of 2, and is faster. HashMap uses this for bucket indexing.
Visual Overview
&& (short-circuit AND): & (non-short-circuit / bitwise):
left is false? always evaluates BOTH sides
→ skip right, return false on booleans: same result, no skip
left is true? on integers: bit-by-bit AND
→ evaluate right, return result
Null guard example:
account != null && account.isActive() → safe (skips if null)
account != null & account.isActive() → NPE! (evaluates both)
Bitwise AND on integers:
0b1010 & 0b1100 = 0b1000 (each bit: 1 only if both are 1)
Shift operators:
100 << 1 = 200 (multiply by 2)
100 >> 1 = 50 (divide by 2, signed)
-1 >>> 1 = MAX (unsigned, fill with 0)
Key Takeaway
The && and || operators short-circuit: they skip the right operand when the left already determines the result. The & and | operators always evaluate both sides. On boolean operands, the result is the same, but the evaluation behavior differs. On integer operands, & and | perform bitwise operations on each pair of bits.
The most important practical consequence is null guards. Writing account != null && account.isActive() is safe because if account is null, the right side is never evaluated. Changing && to & would crash with NullPointerException because & always evaluates both sides.
Use && and || for all boolean logic in if conditions, loop conditions, and ternary expressions. Use &, |, ^, and ~ only for bitwise operations on integers – permission flags, feature masks, and hash calculations. The only time you would use & on booleans is when both sides must be evaluated for their side effects, which is rare and usually a code smell.
One sentence to remember: && short-circuits and is safe for null guards; & always evaluates both sides and is for bitwise operations on integers.
---