☕ Java
Logical Operators
Logical operators combine boolean expressions into more complex conditions. Java has six of them — short-circuit and non-short-circuit variants of AND, OR, and XOR, plus logical NOT. Understanding short-circuit evaluation is essential for writing both safe and efficient conditions.
The Six Logical Operators
Java has six logical operators that work with boolean values:
Java
// Short-circuit versions (most common):
&& // logical AND — true if BOTH operands are true
|| // logical OR — true if AT LEAST ONE operand is true
! // logical NOT — inverts boolean value (unary)
// Non-short-circuit versions (evaluate both sides always):
& // bitwise/logical AND — evaluates both sides
| // bitwise/logical OR — evaluates both sides
^ // logical XOR — true if operands are DIFFERENT&& — Logical AND (Short-Circuit)
&& returns true only if both operands are true. It short-circuits: if the left operand is false, the right operand is never evaluated because the result is already determined to be false.
Java
// Truth table for &&:
System.out.println(true && true); // true
System.out.println(true && false); // false
System.out.println(false && true); // false
System.out.println(false && false); // false
// Short-circuit — right side skipped if left is false:
int x = 0;
boolean result = (x != 0) && (10 / x > 1); // safe — division never happens
// If (x != 0) is false, (10 / x > 1) is never evaluated
// Most important use — null safety:
String name = null;
if (name != null && name.length() > 0) {
System.out.println("Name: " + name);
}
// Without short-circuit, name.length() would throw NullPointerException
// Chained conditions — all must be true:
if (user != null
&& user.isActive()
&& user.hasRole("ADMIN")
&& user.getAge() >= 18) {
grantAccess(user);
}
// Each condition checked only if all previous are true|| — Logical OR (Short-Circuit)
|| returns true if at least one operand is true. It short-circuits: if the left operand is true, the right operand is never evaluated.
Java
// Truth table for ||:
System.out.println(true || true); // true
System.out.println(true || false); // true
System.out.println(false || true); // true
System.out.println(false || false); // false
// Short-circuit — right side skipped if left is true:
boolean result = checkCache() || loadFromDatabase();
// If checkCache() returns true, loadFromDatabase() is never called
// Default value pattern:
String displayName = username != null ? username : "Guest";
// Or with method calls:
String config = System.getenv("APP_NAME") != null
? System.getenv("APP_NAME")
: "DefaultApp";
// Chained fallbacks:
String value = getPrimary() != null ? getPrimary()
: getSecondary() != null ? getSecondary()
: getDefault();
// Validation — any condition being true means invalid:
if (name == null || name.isEmpty() || name.length() > 50) {
throw new IllegalArgumentException("Invalid name");
}! — Logical NOT
! is a unary operator that inverts a boolean value. It's placed before its operand and has very high precedence.
Java
// Basic NOT:
System.out.println(!true); // false
System.out.println(!false); // true
// NOT with expressions:
int x = 5;
System.out.println(!(x > 3)); // false — x > 3 is true, inverted = false
System.out.println(!(x == 10)); // true — x == 10 is false, inverted = true
// NOT in conditions:
boolean isLoggedIn = false;
if (!isLoggedIn) {
redirectToLogin();
}
// Double negation — avoid in practice:
boolean isActive = true;
boolean isNotInactive = !!isActive; // true — same as isActive
// De Morgan's laws — useful for simplifying conditions:
// !(A && B) is equivalent to (!A || !B)
// !(A || B) is equivalent to (!A && !B)
// Example — these are equivalent:
if (!(age < 18 || score < 50)) { } // hard to read
if (age >= 18 && score >= 50) { } // cleaner — De Morgan applied
// NOT with method returns:
List<String> list = new ArrayList<>();
if (!list.isEmpty()) {
System.out.println("Has items: " + list.size());
}^ — Logical XOR
^ (exclusive OR) returns true if the operands are different — one true and one false. Returns false if both are the same. XOR doesn't short-circuit — both sides are always evaluated.
Java
// Truth table for ^ (XOR):
System.out.println(true ^ true); // false — same
System.out.println(true ^ false); // true — different
System.out.println(false ^ true); // true — different
System.out.println(false ^ false); // false — same
// XOR use cases:
// Exactly one of two conditions must be true:
boolean hasCard = true;
boolean hasCash = false;
if (hasCard ^ hasCash) {
System.out.println("Pay with one method"); // exactly one payment method
}
// Toggle a boolean:
boolean flag = true;
flag = flag ^ true; // false — toggles
flag = flag ^ true; // true — toggles back
// Or more simply:
flag = !flag;
// Checking mutual exclusivity:
boolean isAdmin = true;
boolean isGuest = false;
if (isAdmin ^ isGuest) {
// user is either admin OR guest, but not both and not neither
}& and | — Non-Short-Circuit Versions
& and | work like && and || for booleans but always evaluate both operands. Use these only when both sides must execute regardless of the result — usually when side effects are required.
Java
// & — both sides always evaluated:
boolean a = false;
boolean b = sideEffectMethod(); // always called, even though a is false
boolean result = a & b;
// | — both sides always evaluated:
boolean c = true;
boolean d = anotherSideEffect(); // always called, even though c is true
boolean result2 = c | d;
// When to use & and | over && and ||:
// Rarely — only when you need guaranteed side effects on both sides
// Example — two validators that both log independently:
boolean valid = validateName(name) & validateEmail(email);
// Both methods run and log, even if the first one fails
// With &&, second validator would be skipped if first returns false
// For booleans, & and | produce the same logical result as && and ||:
System.out.println(true & false); // false — same as &&
System.out.println(true | false); // true — same as ||
// WARNING — & and | on integers are BITWISE, not logical:
int x = 5, y = 3;
System.out.println(x & y); // 1 — bitwise AND on bits
System.out.println(x | y); // 7 — bitwise OR on bits
// This is completely different from boolean & and |Short-Circuit Evaluation — Practical Patterns
Short-circuit evaluation is one of Java's most practically useful features. These patterns show up constantly in real codebases:
Java
// ── Pattern 1: Null guard ────────────────────────────────────────
if (user != null && user.getProfile() != null && user.getProfile().getEmail() != null) {
sendEmail(user.getProfile().getEmail());
}
// ── Pattern 2: Cache-first loading ───────────────────────────────
String value = cache.get(key) != null ? cache.get(key) : loadAndCache(key);
// Or:
if (cache.containsKey(key) || loadIntoCache(key)) {
return cache.get(key);
}
// ── Pattern 3: Validation chain ──────────────────────────────────
boolean isValid = isNotNull(input)
&& isNotEmpty(input)
&& matchesPattern(input)
&& isUnique(input); // each check only runs if all previous pass
// ── Pattern 4: Short-circuit assignment idiom ─────────────────────
String name = getNameFromCache()
!= null ? getNameFromCache() : fetchNameFromDB();
// ── Pattern 5: Conditional execution ─────────────────────────────
boolean debug = false;
debug && System.out.println("Debug info"); // won't compile — println is void
// Instead:
if (debug) System.out.println("Debug info");
// ── Pattern 6: Guard with OR ──────────────────────────────────────
void process(String input) {
if (input == null || input.isEmpty()) return; // early exit
// safe to use input here
}Related Topics in Java Basics
Bitwise Operators
Bitwise operators work directly on the binary representation of integers — manipulating individual bits. They're used in low-level programming, performance-critical code, flags, masks, and cryptography. Understanding them requires thinking in binary, but the patterns they enable are concise and extremely fast.
Assignment Operators
Assignment operators store values into variables. Java has one basic assignment operator and eleven compound assignment operators that combine an operation with assignment. They're concise, but have specific type behavior — especially the implicit cast in compound assignments — that every Java developer needs to understand.
Unary Operators
Unary operators take a single operand. Java has five: unary plus, unary minus, increment, decrement, and logical NOT. The increment and decrement operators have prefix and postfix forms that behave differently — a distinction that trips up developers at every level.
Ternary Operator
The ternary operator is Java's only three-operand operator. It's a compact conditional expression that returns one of two values based on a boolean condition. Used correctly, it makes code more concise. Overused or nested, it makes code unreadable.