Combining true and false
A single relational expression asks one question. Most real conditions ask several: Is the customer a member AND is the total at least 50? Is the day Saturday OR Sunday? Is the number NOT a multiple of 3? A compound Boolean expression combines Boolean expressions with the three logical operators.
| Operator | Name | a |
b |
Result |
|---|---|---|---|---|
!a |
not | true | false | |
!a |
not | false | true | |
a && b |
and | true | true | true |
a && b |
and | true | false | false |
a && b |
and | false | true | false |
a && b |
and | false | false | false |
a || b |
or | true | true | true |
a || b |
or | true | false | true |
a || b |
or | false | true | true |
a || b |
or | false | false | false |
In words:
!a(“not a”) is true ifais false, and false otherwise.a && b(“a and b”) is true if bothaandbare true, and false otherwise.a || b(“a or b”) is true ifais true,bis true, or both, and false otherwise.
An expression that uses logical operators always evaluates to a boolean value.
Precedence
When an expression has more than one logical operator, Java evaluates ! first, then &&, then ||. Relational operators (<, ==, and so on) and arithmetic are evaluated before any of them. So x < 3 || x > 6 && y % 4 == 0 is read as (x < 3) || ((x > 6) && (y % 4 == 0)). Use brackets whenever the grouping is not obvious; they cost nothing and prevent mistakes. Brackets also let you apply ! to a whole expression: !(x > 5) means “x is not greater than 5”.
Writing ranges
To say that x lies in a range, both ends must hold, so use &&. To say that x is outside a range, either end is enough, so use ||.
x > 2 && x < 8: true only between 2 and 8 (not including 2 or 8).x >= 2 && x <= 8: the same range with its end points included.x < 2 || x > 8: true for everything outside that range.
Each comparison needs its own variable and operator. Natural language lets you say “x is 1 or 2”. Java does not allow x == 1 || 2, because 2 is an int and || needs booleans. Write x == 1 || x == 2. Likewise “x is greater than 5 or less than 3” is x > 5 || x < 3, never x > 5 || < 3.
The always-true trap. The expression x > 3 || x < 8 is true for every number, because each number is greater than 3 or less than 8 (or both). A range needs &&.
Short-circuit evaluation
Java evaluates a compound expression from left to right and stops as soon as the result is certain. This is called short-circuit evaluation.
- In
a && b, ifais false the result is false whateverbis, sobis not evaluated. - In
a || b, ifais true the result is true whateverbis, sobis not evaluated.
You can see it by using a method that announces when it is called.
public class Shortcut {
public static boolean check(String label, boolean value) {
System.out.println("checking " + label);
return value;
}
public static void main(String[] args) {
boolean r1 = check("A", false) && check("B", true);
System.out.println("r1 = " + r1);
boolean r2 = check("C", true) || check("D", false);
System.out.println("r2 = " + r2);
boolean r3 = check("E", true) && check("F", false);
System.out.println("r3 = " + r3);
}
}
Output:
checking A
r1 = false
checking C
r2 = true
checking E
checking F
r3 = false
B and D are never checked: after A is false the && cannot become true, and after C is true the || cannot become false. F is checked because the first operand of && was true, so the second one still decides the answer.
Short-circuiting is more than a curiosity. It lets you put a guard on the left that makes the right side safe:
int count = 0;
int total = 50;
boolean ok = count != 0 && total / count > 5;
System.out.println(ok);
Output:
false
Because count != 0 is false, the division total / count is never evaluated, so there is no divide-by-zero error. Written the other way round, total / count > 5 && count != 0, the division would be evaluated first and the program would throw an ArithmeticException. When the right side can fail, test the safe condition on the left.
A compound condition in an if
A compound expression is used wherever a Boolean expression is expected: in an if, an else if, or on the right of an assignment to a boolean. It often replaces nested ifs:
int hour = 14;
boolean isWeekend = false;
if (hour >= 9 && hour < 17 && !isWeekend) {
System.out.println("open");
} else {
System.out.println("closed");
}
Output:
open
Here three conditions must hold: the hour is at least 9, it is before 17, and it is not the weekend. The equivalent nested version would need three levels of if.
Worked example 1: evaluating compound expressions
Question. Let x = 7 and y = 12. Evaluate each expression.
x > 5 && y < 10x > 5 || y < 10!(x > 5) || y == 12x < 3 || x > 6 && y % 4 == 0(x < 3 || x > 6) && y % 5 == 0!(x == 7 && y == 12)
Solution. First evaluate each relational part, then apply !, then &&, then ||.
| Expression | Parts | Result |
|---|---|---|
| 1 | x > 5 true, y < 10 false |
true && false = false |
| 2 | x > 5 true, y < 10 false |
true || false = true (short-circuits after the left side) |
| 3 | x > 5 true so ! gives false; y == 12 true |
false || true = true |
| 4 | x < 3 false; x > 6 true; y % 4 is 0 so == 0 true |
false || (true && true) = true |
| 5 | x < 3 false, x > 6 true so brackets give true; y % 5 is 2 so == 0 false |
true && false = false |
| 6 | x == 7 true, y == 12 true |
!(true && true) = !true = false |
Check. Items 4 and 5 use the same tests but different grouping, and give different results (true and false). That is exactly why precedence matters: in item 4 && binds first, so the expression is x < 3 or (both of the others); in item 5 the brackets force the || first.
Worked example 2: from rules to code
Question. A year is a leap year if it is divisible by 4, except that years divisible by 100 are not leap years, unless they are also divisible by 400. Write a method isLeap(int year) and test it on 1900, 2000, 2024 and 2026.
Plan. Translate each clause. “Divisible by 4 except if divisible by 100” is year % 4 == 0 && year % 100 != 0. “Unless also divisible by 400” adds a second route to true: || year % 400 == 0.
public class LeapYears {
public static boolean isLeap(int year) {
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
public static void main(String[] args) {
System.out.println(isLeap(1900));
System.out.println(isLeap(2000));
System.out.println(isLeap(2024));
System.out.println(isLeap(2026));
}
}
Output:
false
true
true
false
Trace.
year |
year % 4 == 0 |
year % 100 != 0 |
left group (&&) |
year % 400 == 0 |
result |
|---|---|---|---|---|---|
| 1900 | true | false | false | false (1900 % 400 = 300) | false |
| 2000 | true | false | false | true | true |
| 2024 | true | true | true | not evaluated | true |
| 2026 | false (2026 % 4 = 2) | not evaluated | false | false (2026 % 400 = 26) | false |
Check. 1900 is a century year not divisible by 400, so it is not a leap year. 2000 is divisible by 400, so it is. 2024 is an ordinary leap year, and 2026 is not divisible by 4. The && group is bracketed so that the || is applied last.
Common misconceptions
- “
||means one or the other but not both.” In Java||is also true when both sides are true. - “
x == 1 || 2means x is 1 or 2.” It does not compile. Writex == 1 || x == 2. - “A range uses
||.” A range needs both ends to hold, so it uses&&.x > 3 || x < 8is always true. - “
&&and||evaluate left to right with equal priority.”&&binds tighter than||, and!binds tighter than both. - “Both sides of a logical operator are always evaluated.” Java short-circuits: the right side may never run.
- “The order of the two operands never matters.” For the result alone it does not, but when the right side could fail (such as a division or a method call) the order decides whether a program crashes.
- “
!x > 5means not (x > 5).”!applies to the next operand only, so!x > 5tries to apply!to an int and does not compile. Write!(x > 5).
Where this leads
In Topic 2.6 you will prove that two Boolean expressions are equivalent using truth tables and De Morgan’s laws, which is how you simplify the conditions you wrote here. In Topics 2.7 and 2.8 the same compound conditions control loops. Practise with the practice questions, recap with the revision notes and tick off the checklist. Then read the Topic 2.6 study guide. For nested decisions that compound conditions can sometimes replace, see the Topic 2.4 study guide.
Quick questions
What is the difference between && and ||?
&& (and) is true only when both sides are true. || (or) is true when at least one side is true, including when both are. In ordinary English 'or' sometimes means 'one or the other but not both', but Java's || includes the case where both are true.
What is short-circuit evaluation?
Java stops evaluating a logical expression as soon as the result is certain. If the left side of && is false, the result must be false, so the right side is not evaluated. If the left side of || is true, the result must be true, so the right side is not evaluated.
In what order are !, && and || evaluated?
! first, then &&, then ||. This is why a || b && c means a || (b && c). Brackets change the order.
Why does x > 3 || x < 8 always give true?
Every number is either greater than 3 or less than 8 (or both), so at least one side is always true. To test for a range you need && instead: x > 3 && x < 8.