Module 3 · Control Flow & Program Structure ⏱ 19 min

Boolean Logic & Short-Circuiting

By the end of this lesson you will be able to:
  • Evaluate expressions with &&, ||, and ! and predict their truth value
  • Explain why Java skips evaluating the right side of && and || when the result is already known
  • Recognise when a side effect hidden inside a boolean expression is silently skipped

Every program makes dozens of yes-or-no decisions: is the user logged in? Is the password long enough? Is the cart empty AND the user a guest? Each of those decisions is a boolean expression — an expression that evaluates to either true or false.

Getting boolean logic wrong does not usually crash the program; it silently does the wrong thing. A single misplaced && instead of || can deny access to legitimate users, or worse, grant it to the wrong ones. Senior engineers spend more time reading boolean conditions than writing them, because that is where subtle security and business-logic bugs live.

The three operators

Java gives you three boolean operators. && is AND: the result is true only when both sides are true. || is OR: the result is true when at least one side is true. ! is NOT: it flips a single boolean value, turning true into false and vice-versa.

These operators only work on booleans. You cannot write 5 && 3 in Java; the compiler rejects it. The operands must be boolean variables, comparisons, or method calls that return boolean.

flowchart TD
  AND["&& AND"] --> A1["true && true = true"]
  AND --> A2["true && false = false"]
  AND --> A3["false && ??? = false<br/>short-circuit"]
  OR["|| OR"] --> O1["true || ??? = true<br/>short-circuit"]
  OR --> O2["false || true = true"]
  OR --> O3["false || false = false"]
  NOT["! NOT"] --> N1["!true = false"]
  NOT --> N2["!false = true"]
  style AND fill:#c2410c,color:#fff
  style OR fill:#c2410c,color:#fff
  style NOT fill:#c2410c,color:#fff
Truth tables for &&, ||, and !. Memorise the two short-circuit rows: false && anything is false; true || anything is true.
Main.java — boolean operators in action. Real, compiling Java.
public class Main {
    public static void main(String[] args) {
        int score = 82;
        boolean passed = score >= 50;
        boolean honours = score >= 80;

        System.out.println(passed && honours);
        System.out.println(passed || honours);
        System.out.println(!passed);
        System.out.println(!honours);
    }
}

Short-circuit evaluation: stopping early

Java evaluates && and || from left to right, and it stops as soon as the answer is known. For &&, if the left side is false, the whole expression must be false no matter what the right side is — so Java skips it entirely. For ||, if the left side is true, the whole expression must be true — so the right side is skipped.

This is not just a performance trick; it is a safety feature. Code like if (user != null && user.isAdmin()) relies on short-circuiting: if user is null, the first part is false, Java skips the second part, and the program avoids a NullPointerException. Without short-circuiting, every && and || would force you to write nested if statements.

flowchart LR
  A["user != null && user.isAdmin()"] --> B{"user != null ?"}
  B -->|"false"| C["user.isAdmin() is SKIPPED"]
  C --> D["whole expression = false"]
  B -->|"true"| E["user.isAdmin() runs"]
  E --> F["result = value of isAdmin()"]
  style C fill:#b45309,color:#fff
  style D fill:#b45309,color:#fff
With &&, if the left operand is false the right operand is never evaluated.

Operator precedence and parentheses

Boolean operators have a fixed precedence, just like arithmetic. ! binds tightest, then &&, then ||. That means !a && b is parsed as (!a) && b, and a && b || c && d is parsed as (a && b) || (c && d).

Precedence rules are easy to forget and dangerous to rely on. The compiler will follow them correctly, but the next human reading your code may not. The habit that prevents bugs is simple: when in doubt, add parentheses. They cost nothing and make intent explicit. if ((score > 50) && (attempts < 3)) is immediately readable; if (score > 50 && attempts < 3) forces the reader to recall precedence rules.

Main.java — precedence means && binds tighter than ||. Real, compiling Java.
public class Main {
    public static void main(String[] args) {
        boolean a = true;
        boolean b = false;
        boolean c = true;

        System.out.println(a || b && c);
        System.out.println((a || b) && c);
        System.out.println(!b && c);
    }
}
flowchart TD
  A["if (registerUser() && sendEmail())"] --> B{"registerUser()<br/>returns false?"}
  B -->|"yes"| C["sendEmail() NEVER RUNS"]
  C --> D["user registered,<br/>no email sent"]
  B -->|"no"| E["sendEmail() runs"]
  style C fill:#b45309,color:#fff
  style D fill:#b45309,color:#fff
The classic trap: a method call inside a boolean expression is skipped when short-circuiting applies.
Exercise

What does this program print? Read it carefully and type the exact output (three lines).

public class Main {
    public static void main(String[] args) {
        int x = 7;
        int y = 3;
        System.out.println(x > 5 && y < 5);
        System.out.println(x > 5 || y > 5);
        System.out.println(!(x > 5));
    }
}
Exercise

In false && someMethod(), does someMethod() run?

Exercise

Short-circuit side-effect check. bump() prints and returns a value. Predict the exact output (five lines) — watch which calls run and which are skipped.

public class Main {
    static int counter = 0;

    static boolean bump() {
        counter++;
        System.out.println("bump");
        return counter >= 2;
    }

    public static void main(String[] args) {
        counter = 0;
        boolean a = bump() && bump();
        System.out.println(a);
        counter = 0;
        boolean b = bump() || bump();
        System.out.println(b);
    }
}
Exercise

What is the value of true || false && false?

Exercise

Nested boolean puzzle. Predict the exact output (three lines) — work from the innermost parentheses outward.

public class Main {
    public static void main(String[] args) {
        int a = 5;
        int b = 10;
        int c = 15;
        boolean r1 = (a < b) && (b < c);
        boolean r2 = !(a > b) || (c < a);
        boolean r3 = (a == 5) && (b == 5) || (c == 15);
        System.out.println(r1);
        System.out.println(r2);
        System.out.println(r3);
    }
}

Recap

  • && is AND: both sides must be true. || is OR: at least one side must be true. ! flips a single boolean.
  • Java short-circuits && and ||: the right side is evaluated only if the left side does not already decide the result.
  • Short-circuiting makes user != null && user.isActive() safe from NullPointerException.
  • ! binds tightest, then &&, then ||. Use parentheses to make complex conditions readable.
  • Never hide actions (side effects) inside boolean expressions — they may be silently skipped.

Next you will meet switch in depth: classic fall-through, modern expressions, and the enum type that makes switches cleaner and safer.

Checkpoint quiz

Why does if (user != null && user.isAdmin()) not throw a NullPointerException when user is null?

What is the value of !false && true || false?

Go deeper — technical resources