Command Palette

Search for a command to run...

PHASE 1Beginner ~28 min· topic 8 of 14

Topic 1.8

Comparison and Logical Operators

In one line

Comparison operators (==, !=, <, >, <=, >=) ask a yes/no question and produce a boolean; logical operators (&&, ||, !) combine those answers. && and || short-circuit, skipping the right side when the left already decides the result, which is both an optimisation and a safety tool.

Think of it like this

The rule at a theme-park ride is 'at least 120 cm tall AND has a ticket'. The attendant measures your height first. If you're too short, she doesn't bother asking for a ticket, because the answer is already no. That's short-circuit evaluation: tallEnough && hasTicket never checks the ticket when tallEnough is false.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Comparison operator
An operator that compares two values and answers true or false, like < or ==.
Logical operator
An operator that combines booleans: && (and), || (or), ! (not).
Condition
A boolean expression used to decide something, like age >= 18.
Short-circuit
Skipping the right side of && or || when the left side already decides the answer.
Reference
The 'address' Java uses to reach an object. Two variables can hold references to the same object or to two different ones.
Guard
A check placed first in a condition to protect the checks after it, like x != null &&.
Truth table
A small table listing the result of a logical operator for every combination of inputs.
De Morgan's laws
Two rules for negating combined conditions: 'not (A and B)' is 'not A or not B', and 'not (A or B)' is 'not A and not B'.

Step by step

01Every comparison is a question with a boolean answer

age >= 18 is an expression of type boolean, just like age + 1 is an expression of type int. You can store it (boolean adult = age >= 18;), print it, return it, or use it in an if.

Don't write if (adult == true); adult is already a boolean. Likewise, instead of if (x > 5) return true; else return false; write return x > 5;.

02== on primitives vs objects

For int, double, char and the other primitives, == compares the values: 3 == 3 is true.

For objects, the variables hold references, and == compares the references. Two different String objects with the same letters are not ==. That's why you must use a.equals(b) for Strings; Topic 6.2 shows when == happens to work and why you mustn't rely on it.

The same trap hides in wrapper types: Integer values from -128 to 127 are cached and shared, so == happens to work for them, but Integer 128 objects are usually distinct. Use .equals() or compare ints.

== on primitives vs objectsdiagram
Rendering diagram…

03Truth tables for && || ^ !

&&: true only for true && true. ||: false only for false || false. ^: true when the two sides differ. !: flips.

Read conditions out loud: isMember || total > 1000 is 'members, or anyone spending over 1000'. If the English sounds wrong, the code probably is.

Truth tables for && || ^ !diagram
Rendering diagram…

04Short-circuit evaluation

For left && right, Java evaluates left first. If it's false, the result must be false, so right is never evaluated. For left || right, if left is true, right is skipped.

This is a guarantee of the language, not an optimisation the compiler may or may not do, so you can rely on it: if (list != null && !list.isEmpty()), if (i < arr.length && arr[i] > 0), if (count != 0 && total / count > 10).

Order matters: put the cheap or protective check first. name.length() > 3 && name != null crashes with a NullPointerException when name is null, because the protective check comes too late.

Short-circuit evaluationdiagram
Rendering diagram…

05Non-short-circuit & and |

& and | on booleans compute the same results as && and || but always evaluate both sides. That's occasionally useful when the right side has a side effect that must always happen, but it surprises readers, so prefer two separate statements.

On integers, &, | and ^ are bitwise operators (Topic 1.9). The same symbols mean different things depending on the operand types.

06Precedence: when to add brackets

&& binds tighter than ||, like * binds tighter than +. isAdmin || isOwner && isActive means isAdmin || (isOwner && isActive), so an inactive admin passes. If you meant (isAdmin || isOwner) && isActive, you need the brackets.

Comparisons bind tighter than && and ||, so x > 0 && x < 10 needs no brackets. But == binds tighter than bitwise &, which bites in flags & MASK != 0 (Topic 1.9). When in doubt, add brackets; they cost nothing.

07Negating conditions with De Morgan

To stop a loop when done or failed, you loop while !(done || failed), which De Morgan rewrites as !done && !failed. To reject input outside 1..10, the opposite of x >= 1 && x <= 10 is x < 1 || x > 10.

A common bug is negating only half: the opposite of x >= 1 && x <= 10 is not x < 1 && x > 10 (which is never true).

Try it yourself

  1. 1

    Flip the order

    In the short-circuit example, swap the operands of the first && to check("B", true) && check("A", false). Predict which lines print. (Both checks run, because the left side is true.)

  2. 2

    Write a leap-year test

    A year is a leap year if it's divisible by 4 and not by 100, or divisible by 400. Write it as one boolean expression and test 1900 (false), 2000 (true), 2024 (true), 2026 (false).

    One answer: (y % 4 == 0 && y % 100 != 0) || y % 400 == 0.

  3. 3

    Apply De Morgan

    Rewrite !(age < 13 || age > 19) without the outer !. Check your answer gives the same result for ages 12, 13, 19 and 20.

Code & diagrams

Comparisons produce booleans New tab
Sign in to run this example in your browser.

Expected output

age > 12:  true
age >= 18: false
age == 15: true
age != 15: false
10 == 10.0: true
'a' == 97:  true
'a' < 'b':  true
NaN < 1 || NaN >= 1: false
adult: false
Watch short-circuiting happen New tab
Sign in to run this example in your browser.

Expected output

&& with false first:
  checking A
result false
|| with true first:
  checking A
result true
& always evaluates both:
  checking A
  checking B
result false
no long name, and no crash
Precedence, De Morgan and the Integer cache New tab

The -128 to 127 cache is the default; it can be raised with -XX:AutoBoxCacheMax, which is one more reason never to rely on == for Integer.

Sign in to run this example in your browser.

Expected output

a || b && c   = true
(a || b) && c = false
De Morgan holds: true
true ^ true = false
in range: true, out of range: false
Integer 127 == 127: true
Integer 128 == 128: false
128 equals 128:     true

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Use = instead of == in a condition

Write if (x = 5) meaning 'if x equals 5'.

terminal
$ javac Main.java
── what you'll see ──
Main.java:4: error: incompatible types: int cannot be converted to boolean
if (x = 5) {
^
1 error

Break #2

Chain a comparison like in maths

Write if (0 < x < 10).

terminal
$ javac Main.java
── what you'll see ──
Main.java:4: error: bad operand types for binary operator '<'
if (0 < x < 10) {
^
first type: boolean
second type: int
1 error

Break #3

Put the null check second

Write if (name.length() > 3 && name != null) with name = null.

terminal
$ javac -g Main.java
java Main
── what you'll see ──
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "name" is null
at Main.main(Main.java:4)
Without -g the message says "<local1>" instead of "name", because local variable names are only kept as debug information.

Myth vs fact

Myth

== checks whether two Strings have the same text.

Fact

For objects, == checks whether both references point to the same object. Use .equals() for content.

Myth

& and && are the same for booleans.

Fact

They give the same result, but && skips the right side when the left is false; & always evaluates both.

Myth

The compiler might evaluate the right side of && first for speed.

Fact

Left-to-right short-circuit evaluation is guaranteed by the Java Language Specification.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    && and || compile to conditional jumps (ifeq, ifne), not to boolean operations, so there's no 'and' instruction involved. & on booleans compiles to iand on 0/1 ints, which is why it evaluates both sides.

  • ▸

    Floating-point comparisons follow IEEE 754: every comparison involving NaN is false except !=, which is true. So !(x < y) is not the same as x >= y when NaN is possible; Double.compare gives a total order (NaN greater than everything, -0.0 less than 0.0).

  • ▸

    The Integer cache range (-128 to 127) is required by JLS §5.1.7 for boxing; the upper bound can be raised with -XX:AutoBoxCacheMax or java.lang.Integer.IntegerCache.high, so == on boxed values can behave differently between JVM configurations.

  • ▸

    Helpful NullPointerException messages (JEP 358) arrived in Java 14 and became the default in Java 15. They name the null expression; local variable names appear only if the class was compiled with -g, otherwise you see <local1>.

Remember this

  1. 1

    Relational operators <, >, <=, >= compare numbers (including chars, which are numbers). Equality operators == and != work on every type. All of them produce a boolean. Mixed numeric types are promoted first, so 10 == 10.0 is true and 'a' == 97 is true.

  2. 2

    For primitives, == compares values. For objects, == compares references: it asks 'are these the very same object?', not 'do they hold the same data?'. Use .equals() to compare Strings and other objects by content (Topic 6.2 and Topic 4.8).

  3. 3

    **&& (and) is true only if both sides are true; || (or) is true if at least one side is; ! (not)** flips a boolean. && and || evaluate the left side first and skip the right side if the result is already known. This lets you write guards like s != null && s.length() > 0, where the second check would crash without the first.

  4. 4

    &, | and ^ also work on booleans but always evaluate both sides. ^ (exclusive or) is true when exactly one side is true. Use them only when you deliberately need both sides to run.

  5. 5

    Precedence (high to low): !, then < > <= >=, then == !=, then &, ^, |, then &&, then ||. So a || b && c means a || (b && c). Comparisons can't be chained: 0 < x < 10 doesn't compile; write x > 0 && x < 10.

  6. 6

    De Morgan's laws let you flip conditions safely: !(a && b) equals !a || !b, and !(a || b) equals !a && !b. They help turn 'keep looping while not done' into clear code, and they show up in every interview about conditions.

Explain it without notes

01

What does short-circuit evaluation mean, and give a real example where it prevents a crash.

02

Why does == work for int but not reliably for String or Integer?

03

What is the value of true || false && false, and why?

04

Negate x >= 1 && x <= 10 correctly and explain the rule you used.

Practice

01

Print whether each of the years 1900, 2000, 2024 and 2026 is a leap year.

02

A shop gives free delivery to members, or to anyone whose order is at least 500, but never to blocked customers. Write the condition and test three customers.

03

Given String s = null, print "empty" if s is null or has length 0, and "has text" otherwise, without crashing. Then test with "hi".

Trade-offs

  • ↔

    Short-circuit operators make guards safe and skip needless work, but a right-hand side with side effects (like i++) may silently not run; keep side effects out of conditions.

  • ↔

    Long single-line conditions are compact but hard to read; naming parts (boolean eligible = ...; boolean blocked = ...;) costs a few lines and makes logic reviewable.

Done when you can

  • I can use all six comparison operators and know they produce booleans.

  • I know == compares values for primitives and references for objects.

  • I can predict when && and || skip their right side and use guards correctly.

  • I know operator precedence for !, comparisons, && and ||, and add brackets when unclear.

  • I can negate a compound condition with De Morgan's laws.