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
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
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.
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
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));
}
}
x > 5 is true, y < 5 is true, so true && true is true.
x > 5 is true, so true || anything is true without evaluating the right side.
!(true) is false.
In false && someMethod(), does someMethod() run?
&& short-circuits: when the left side is false, the whole expression must be false, so Java skips the right side entirely. This is why user != null && user.isAdmin() is safe.
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);
}
}
First bump() returns false (counter becomes 1, which is < 2), so && skips the second bump. That chain prints 'bump' once.
Second chain resets counter, first bump() returns false, so || does NOT short-circuit — it must evaluate the second bump (counter becomes 2, now >= 2, returns true). That chain prints 'bump' twice.
Count the prints, not the chains: && skipped a call, || did not. Five lines in total.
What is the value of true || false && false?
&& binds tighter than ||, so the expression is parsed as true || (false && false). false && false is false, then true || false is true. If you meant (true || false) && false, you need parentheses.
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);
}
}
r1: 5 < 10 is true, 10 < 15 is true, so true && true = true.
r2: !(5 > 10) is !false = true, so true || anything is true (short-circuit).
r3: && binds tighter than ||. (5 == 5) && (10 == 5) is true && false = false. Then false || (15 == 15) is false || true = true.
Recap
&&is AND: both sides must betrue.||is OR: at least one side must betrue.!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 fromNullPointerException. !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.