Short-circuit evaluation
On This Lesson 11 topics
Lesson Overview
In Lesson 2 you learned that a && b is true only when both sides are true. Look at that again from Java's point of view: if a is already false, the answer is false no matter what b is. Looking at b would be wasted work. So Java does not look. This is short-circuit evaluation: the right side of && and || is evaluated only when the left side has not already decided the answer.
Think of a security guard checking two things at a door. If the first check fails, there is no point in doing the second. Even better, the first check can protect the second: "if the room exists, check what is in it". Used on purpose, short-circuiting lets you write conditions that never crash.
Why this matters: backend code constantly meets missing data, such as a zero count, an empty value, or a value that was never set. Putting the safety check on the left of && is one of the most common ways to avoid a crash, and one of the first things a reviewer looks for.
This lesson builds on Lessons 2 and 4: boolean operators and precedence, and on the division and String ideas from earlier modules.
Learning Objectives
By the end of this lesson you will be able to:
- State exactly when the right operand of
&&and of||is skipped. - Use
&&and||to guard a risky operation, such as division by a value that might be zero. - Explain why the order of operands matters with a crash example.
- Explain why a side effect in the right operand may never run.
- Distinguish
&&and||from the non-short-circuit&and|. - Write a null check for a
Stringso that its method is never called onnull.
Written Explanation
The two rules
| Expression | The right side is evaluated when... | Otherwise |
|---|---|---|
a && b | a is true | a is false, so the result is false immediately |
a || b | a is false | a is true, so the result is true immediately |
The Java Language Specification states both rules: the conditional-and operator evaluates its right operand only if the left is true, and the conditional-or operator evaluates its right operand only if the left is false. The result of the whole expression is always what the truth table from Lesson 2 gives. Short-circuiting changes only whether the right side runs.
When the right side is a plain comparison with no risk, you cannot see the difference. It becomes visible when the right side could fail or could change something.
Protecting a risky operation
Recall from Lesson 1 that 10 / d with int d = 0 throws an ArithmeticException. Put the safety check first:
d != 0 && 10 / d > 1When d is 0, the left side d != 0 is false, so Java never evaluates 10 / d. The whole expression is false, and nothing crashes. When d is not zero, the left side is true, and Java goes on to check the right side. Read it as a sentence: "d is not zero, and ten divided by d is more than one".
For || the guard is the other way round. d == 0 || 10 / d > 1 reads "d is zero, or ...". If d is zero, the left is true, the right side is skipped, and the answer is true.
Order matters
Swap the operands and the guard no longer protects anything:
10 / d > 1 && d != 0 // crashes when d is 0Java evaluates left to right (Lesson 4), so it evaluates 10 / d > 1 first, and with d equal to zero that throws before d != 0 is ever reached. The two expressions mean the same thing in mathematics and behave differently in Java. The rule: put the cheap, safe check on the left, and the risky one on the right.
Protecting a method call on null
A variable that holds a reference (such as a String) can hold null, meaning "no object". Calling a method on null throws a NullPointerException. The same guard works:
name != null && name.length() > 3If name is null, the right side is not evaluated, and the answer is false. This is one of the most common forms of short-circuiting in real code. (The full story about null comes later; for now you only need this pattern.)
Side effects in the right operand
If the right operand contains an increment or an assignment, whether it runs depends on the left side. This is called a side effect, because it changes something besides producing a value:
int count = 0;
boolean r = false && count++ > 0; // count++ is skipped: count stays 0Code that depends on a side effect inside a condition is fragile: it does something only some of the time, and the next reader will not notice. Keep conditions free of side effects. Do the increment on its own line.
The non-short-circuit versions: & and |
Java also has single-character & and |. On boolean operands they give the same answer as && and ||, but they always evaluate both sides. The Java Language Specification describes && as working like & except for the skipping of the right operand. You will almost never want the single versions for conditions. If you type one by mistake, a guard like d != 0 & 10 / d > 1 crashes when d is zero, even though it looks almost the same. (These operators have another job with the bits of numbers, which this course does not use.)
Examples
Example 1: the guard in both positions (simple)
public class SafeDiv {
public static void main(String[] args) {
int d = 0;
System.out.println(d != 0 && 10 / d > 1);
System.out.println(d == 0 || 10 / d > 1);
d = 5;
System.out.println(d != 0 && 10 / d > 1);
d = 20;
System.out.println(d != 0 && 10 / d > 1);
}
}Output (JDK 21):
false
true
true
falseWith d zero, both guards avoid the division. With d = 5, 10 / 5 is 2, and 2 > 1 is true. With d = 20, 10 / 20 is 0 (integer division, Lesson 1), and 0 > 1 is false. Now the same condition written in the wrong order:
public class Crash {
public static void main(String[] args) {
int d = 0;
System.out.println(10 / d > 1 && d != 0);
}
}Exception in thread "main" java.lang.ArithmeticException: / by zero
at Crash.main(Crash.java:4)The exception message names the line of the crash. Reading the first line of such a message and the line number is the start of debugging, which a later module teaches properly.
Example 2: side effects that do or do not run (practical)
public class Side {
public static void main(String[] args) {
int count = 0;
boolean r1 = false && count++ > 0;
System.out.println(r1 + " count=" + count);
boolean r2 = true || count++ > 0;
System.out.println(r2 + " count=" + count);
boolean r3 = true && count++ >= 0;
System.out.println(r3 + " count=" + count);
boolean r4 = false & count++ >= 0;
System.out.println(r4 + " count=" + count);
boolean r5 = true | count++ >= 0;
System.out.println(r5 + " count=" + count);
}
}false count=0
true count=0
true count=1
false count=2
true count=3The first two lines show a skipped right side: count stays 0. Line 3 shows && running the right side because the left was true. Lines 4 and 5 use & and |: the right side ran even though the answer was already known, so count kept going up. The increment inside the condition is only here to make the skipping visible; in real code, avoid it.
Example 3: null and zero guards in a service (realistic)
public class NullSafe {
public static void main(String[] args) {
String name = null;
boolean safe = name != null && name.length() > 3;
System.out.println(safe);
String other = "Amira";
System.out.println(other != null && other.length() > 3);
int items = 0;
int totalCents = 500;
boolean expensive = items > 0 && totalCents / items > 100;
System.out.println(expensive);
System.out.println(name == null || name.length() == 0);
}
}false
true
false
trueThis models two everyday checks: "does the customer have a usable name?" and "is the average item price above one dollar?" (with an empty cart, the average has no meaning, and the guard items > 0 prevents the division). The last line is a typical "is it missing or empty?" test. Because of ||, name.length() runs only when name is not null. Written the other way round, it would crash:
System.out.println(name.length() > 3 && name != null);Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "<local1>" is null
at NullCrash.main(NullCrash.java:4)The name <local1> appears because the program was compiled without extra debugging information. The exact wording of such messages can differ between Java versions and compile options.
Backend Insight
Almost every request a server handles carries data that might be missing, empty, or zero: an optional query parameter, a record that was not found, a count of zero items. Guard conditions with && and || are the quickest way to protect the code behind them. A database result that may be null, a divisor from user input, an index that might be out of range: each is checked on the left before it is used on the right.
Two review habits follow:
- Safe check first. Reviewers read
x != null && x.something()as correct andx.something() && x != nullas a bug waiting to happen. - No side effects in conditions. Short-circuiting means a side effect may or may not run, depending on data. That is how a bug appears only for some users, and it is why the next lesson's habit, predict first and run second, matters so much for conditions.
Common Mistakes
Mistake 1: Putting the guard on the wrong side
- Incorrect:
10 / d > 1 && d != 0 - Correct:
d != 0 && 10 / d > 1
Mistake 2: Using & or | by accident
A single character always evaluates both sides, so the guard no longer protects. In conditions, write && and ||.
Mistake 3: Using || as a guard when && is needed
d == 0 || 10 / d > 1 is safe but means something different: it is true when d is zero. Choose the operator by the meaning you want, then order the operands for safety.
Mistake 4: Hiding important work in the right operand
isValid && save(item) style conditions do something only some of the time. Put the important action on its own line.
Exercises
Try each exercise yourself before opening the solutions. Predict first.
Level A: Foundation
A1. In a && b, when is b skipped? In a || b?
Show solution A1
A1. b is skipped in a && b when a is false; in a || b when a is true.
A2. int n = 0; Evaluate n != 0 && 100 / n > 5 and say whether it crashes.
Show solution A2
A2. n != 0 is false, so the right side is skipped. The result is false and there is no crash.
A3. Evaluate true || false, false && true, true && false || true.
Show solution A3
A3. true, false, true (&& first: true && false is false, then false || true is true).
Level B: Application
B1. int total = 80; int count = 0; Write a condition that is true only when the average total / count is at least 10 and never crashes.
Show solution B1
B1. count != 0 && total / count >= 10. With count = 0 it is false; the division is skipped.
B2. What does this print? int k = 5; boolean ok = k > 10 && k++ > 0; System.out.println(ok + " " + k); Then change && to & and say what it prints.
Show solution B2
B2. With &&: false 5, because k > 10 is false and k++ is skipped. With &: false 6, because both sides are evaluated and k++ runs.
B3. String code = null; Write a condition that is true when code is null or empty (length zero), without crashing.
Show solution B3
B3. code == null || code.length() == 0. If code is null the left side is true and the method call is skipped.
Level C: Challenge
C1. int a = 4; int b = 0; Evaluate each and say which crash: (i) b != 0 && a / b > 1 (ii) a / b > 1 || b == 0 (iii) b == 0 || a / b > 1 (iv) a > 0 & a / b > 1
Show solution C1
C1. (i) false, no crash: the guard skips the division. (ii) crashes: a / b is evaluated first and b is zero. (iii) true, no crash: the left side is true, so the right is skipped. (iv) crashes: & evaluates both sides, so the division runs even though a > 0 is already known.
C2. A rule says an order can be refunded if the order exists (order != null), it is not older than 30 days (order.ageDays <= 30), and it is paid. Write the condition with the order's days written as days, a boolean orderExists, and boolean paid, so that the cheap, safe checks come first. Explain your ordering in one sentence.
Show solution C2
C2. orderExists && days <= 30 && paid. Everything on the right of orderExists is only looked at once the order is known to exist (in real code, reading order.ageDays directly requires it); cheap comparisons come before anything slower, and no side effects are used.
Mini Challenge
Write a program Guards with int items = 0; int totalCents = 1200; String note = null;.
- Print whether the average item price is at least 100 cents, written so that it cannot crash.
- Print whether the note is present and longer than 5 characters, written so that it cannot crash.
- Print whether the note is missing or empty.
- Write one version of (1) with the operands in the wrong order, but do not run it. Say in a comment what would happen and why.
- Change
itemsto 3 andnoteto"Gift wrap"and predict the three results first.
Check your work: the three printed results with the first values should be false, false, and true; with the second values, they should be true, true, and false.
Summary & Next Lesson
Key takeaways
a && bskipsbwhenaisfalse;a || bskipsbwhenaistrue.- Put the safety check on the left and the risky operation on the right:
d != 0 && 10 / d > 1. - The same pattern protects method calls on a possibly
nullvalue. - A side effect in the right operand may not run; keep conditions free of them.
&and|always evaluate both sides, so do not use them as guards.
Next lesson
Next is the module demo, "Demo: predict-then-run expression challenge". You will put every operator from this module together, predict a set of results on paper, and then compare them with real output.
Self-check
- When does Java skip the right side of
||? - Why is
d != 0 && 10 / d > 1safe, and why is10 / d > 1 && d != 0not? - What is the difference between
&&and&on booleans? - What would
name != null && name.isEmpty()give ifnameisnull? - Why should you avoid
count++inside a condition?
Show answers
- When the left side is
true, because the whole expression must then betrue. - In the first, a zero
dmakes the left sidefalse, so the division is skipped. In the second, the division is evaluated first and throws whendis zero. - Both give the same result, but
&&skips the right side when the left isfalse, and&always evaluates both. false, with no crash: thenullcheck fails first and the method call is skipped.- It may or may not run depending on the left side, which makes the program's behavior depend on hidden conditions and is hard to read.
References
- Oracle, The Java Language Specification, Java SE 21, Chapter 15: Expressions. Section 15.23 says
&&evaluates the right operand only if the left istrue; section 15.24 says||evaluates its right operand only if the left isfalse; section 15.17.2 says integer division by zero throwsArithmeticException. - Oracle, The Java Tutorials: Operators. It lists
&&and||in the precedence table. It does not explain short-circuiting itself; that comes from the specification above. - All outputs and exception messages above came from compiling and running the code with JDK 21. The text of a
NullPointerExceptionmessage depends on the Java version and on how the code was compiled. Calling a method onnullthrowingNullPointerExceptionwas observed in the run, and I did not look it up on a documentation page here.
Practice
Apply this lesson with the module's lab, exercises and quiz.
Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.