Command Palette

Search for a command to run...

PHASE 2Beginner ~33 min· topic 1 of 10

Topic 2.1

Making Decisions with if and else

In one line

An if statement runs a block of code only when a boolean condition is true; else and else if add the other paths. Java checks the conditions top to bottom, runs the first branch that matches, and refuses any condition that isn't a real boolean.

Think of it like this

Before leaving home you look out of the window. If it is raining, you take an umbrella. Otherwise (else), you take sunglasses. You never take both, and you always take exactly one. An if/else in Java is that same choice: look at a fact, then take one path.

Words you'll meet

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

Control flow
The order in which the statements of a program run. if, switch and loops change that order.
Condition
An expression that is either true or false, written in the round brackets after if.
Block
A group of statements inside curly braces { }. Variables declared inside a block exist only inside it.
Branch
One of the possible paths through an if/else. Exactly one branch of an if/else runs.
else-if ladder
A chain of if ... else if ... else if ... else tests, checked from top to bottom.
Nested if
An if statement placed inside another if or else block.
Definite assignment
The compiler rule that a local variable must surely have a value on every path before you read it.
Guard clause
An early check at the top of a piece of code that handles a special case (often by returning) so the main logic stays flat.

Step by step

01A one-way decision: plain if

The simplest form runs a block only when the condition is true. When it is false, Java skips the block and carries on with the next statement.

Read if (temperature > 30) aloud as 'if temperature is greater than thirty'. The > is a comparison operator (Topic 1.8) and produces a boolean.

Main.javawhole filejava
public class Main {
    public static void main(String[] args) {
        int temperature = 34;
        if (temperature > 30) {
            System.out.println("It's hot. Drink water.");
        }
        System.out.println("Have a nice day.");
    }
}
terminal
$ java Main.java
── expected output ──
It's hot. Drink water.
Have a nice day.
Change 34 to 20 and run again: only the second line prints.

02A two-way decision: if and else

else is the 'otherwise' path. Exactly one of the two blocks runs, never both and never neither.

Notice that else has no condition of its own. It means 'every case the if didn't catch'.

Main.javawhole filejava
int age = 15;
if (age >= 18) {
    System.out.println("You can vote.");
} else {
    System.out.println("Not yet. " + (18 - age) + " years to go.");
}
terminal
$ java Main.java
── expected output ──
Not yet. 3 years to go.

03Many paths: the else-if ladder, and why order matters

To choose among more than two paths, chain else if. Java tests each condition in order and stops at the first true one.

Because it stops at the first match, the order is part of the logic. If you test score >= 60 before score >= 90, a score of 95 matches the first test and gets a D. Always test the narrowest range first, or write ranges that can't overlap.

Main.javawhole filejava
int score = 82;
String grade;
if (score >= 90) {
    grade = "A";
} else if (score >= 80) {   // only reached when score < 90
    grade = "B";
} else if (score >= 70) {   // only reached when score < 80
    grade = "C";
} else {
    grade = "F";
}
System.out.println(grade);   // B
Many paths: the else-if ladder, and why order mattersdiagram
Rendering diagram…

04Braces, the one-statement rule, and the dangling else

Without braces, an if controls exactly one statement. Indentation means nothing to the compiler. The second line below always runs, even though it looks like part of the if.

An else pairs with the nearest if that has no else yet. In the second snippet the else belongs to if (raining), not to if (temp > 10), so when temp is 5 nothing prints at all. This is the dangling else problem, and it is the main reason most style guides (Google's included) require braces on every if.

Main.javawhole filejava
int temp = 5;
boolean raining = false;

if (temp > 30)
    System.out.println("Hot!");
    System.out.println("Stay inside.");   // NOT inside the if: always runs

if (temp > 10)
    if (raining)
        System.out.println("Take an umbrella.");
else                                       // pairs with if (raining)!
    System.out.println("Cold!");
terminal
$ java Main.java
── expected output ──
Stay inside.

05The condition must be a boolean

In C, if (x = 5) assigns 5 to x and then treats 5 as 'true'. That typo has caused countless bugs. Java closes the door: x = 5 has type int, and an int is not a boolean, so the code doesn't compile.

One version of the typo still slips through: when the variable itself is a boolean. if (done = true) assigns true to done and the condition is always true. Write if (done) and if (!done) instead of comparing booleans with ==, and the typo can't happen.

Main.javawhole filejava
int x = 3;
if (x = 5) { }          // compile error: int cannot be converted to boolean

boolean done = false;
if (done = true) {      // compiles! assigns true, always runs
    System.out.println("Always printed");
}
if (done) { }           // the clean way to test a boolean

06Definite assignment: the compiler checks every path

Local variables (variables declared inside a method) have no default value. Before you read one, the compiler proves that every possible path assigned it. If any path might skip the assignment, it's a compile error.

Below, when x > 2 is false, s is never assigned, so println(s) is rejected. Adding an else that assigns s fixes it, because now both paths assign. This check is done entirely at compile time and costs nothing at run time.

Main.javawhole filejava
int x = 5;
String s;
if (x > 2) {
    s = "big";
}
System.out.println(s);   // error: variable s might not have been initialized

// Fixed: every path assigns s
String t;
if (x > 2) {
    t = "big";
} else {
    t = "small";
}
System.out.println(t);   // big

07What the compiler really produces

The JVM has no if keyword. javac turns an if into a conditional jump: compare two values and, if a test holds, continue at another instruction. Look at the bytecode with javap -c (it ships with the JDK).

Notice the flipped test. The source says age >= 18; the bytecode says if_icmplt 9, 'if age is less than 18, jump to instruction 9' (the else-branch). The then-branch is the code that falls straight through. This is why an if is extremely cheap: one compare and one jump.

Main.javawhole filejava
static String check(int age) {
    if (age >= 18) {
        return "adult";
    } else {
        return "minor";
    }
}
terminal
$ javac Main.java
javap -c Main
── expected output ──
static java.lang.String check(int);
Code:
0: iload_0
1: bipush 18
3: if_icmplt 9
6: ldc #7 // String adult
8: areturn
9: ldc #9 // String minor
11: areturn

08Flat beats deep: guard clauses

Deeply nested ifs are hard to read: by the fourth level you've forgotten what the first one checked. A guard clause handles the special case first and leaves early, so the main path stays at the left margin.

You'll write guards with return once you meet methods (Phase 3) and with continue inside loops (Topic 2.8). The idea is the same: 'get the odd cases out of the way first'.

Main.javawhole filejava
// Nested: the real work is buried three levels deep
static String ship(String address, int items, boolean paid) {
    if (address != null) {
        if (items > 0) {
            if (paid) {
                return "Shipping " + items + " items";
            } else { return "Not paid"; }
        } else { return "Empty cart"; }
    } else { return "No address"; }
}

// Guard clauses: same behaviour, flat and readable
static String shipFlat(String address, int items, boolean paid) {
    if (address == null) return "No address";
    if (items <= 0)      return "Empty cart";
    if (!paid)           return "Not paid";
    return "Shipping " + items + " items";
}

Try it yourself

  1. 1

    Predict, then run the ladder

    Open 'Grades with an else-if ladder'. Before running, write down the grade for 89, 90 and 59. Add three println lines for them and run. Were the boundary values (90 exactly, 59) what you expected?

  2. 2

    Break the order on purpose

    In the grade method, swap the score >= 90 and score >= 60 rungs. Predict which grades change, then run. Only scores of 60 and above are affected, and they all become D.

  3. 3

    Remove the braces

    In 'The dangling else' example, set temp = 20 and raining = false. Predict both outputs before you run. (Without braces you'll now see Cold! even though it's 20 degrees, because the else belongs to if (raining).)

Code & diagrams

Grades with an else-if ladder New tab

`grade` is a small helper method (Phase 3 covers methods). Each call runs the ladder once.

Sign in to run this example in your browser.

Expected output

95 -> A
80 -> B
79 -> C
60 -> D
12 -> F
Wrong order vs right order New tab
Sign in to run this example in your browser.

Expected output

wrong order: D
right order: A
The dangling else, with and without braces New tab
Sign in to run this example in your browser.

Expected output

Without braces:
With braces:
  Cold!
Combined conditions: leap years and a null guard New tab

The `for (int year : years)` loop just repeats the check for each year; Topic 2.7 explains it.

Sign in to run this example in your browser.

Expected output

1900 is not a leap year
2000 is a leap year
2024 is a leap year
2025 is not a leap year
no name or short name
The bytecode of an if (javap -c)text
static java.lang.String check(int);
  Code:
     0: iload_0                 // push age
     1: bipush        18        // push 18
     3: if_icmplt     9         // if age < 18 jump to 9 (the else branch)
     6: ldc           #7        // String adult
     8: areturn
     9: ldc           #9        // String minor
    11: areturn

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) where x is an int.

Main.javawhole filejava
public class Main {
    public static void main(String[] args) {
        int x = 3;
        if (x = 5) {
            System.out.println("five");
        }
    }
}
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

Assign a variable on only one path

Declare String s;, assign it only inside an if without an else, then print it.

Main.javawhole filejava
public class Main {
    public static void main(String[] args) {
        int x = 5;
        String s;
        if (x > 2) { s = "big"; }
        System.out.println(s);
    }
}
terminal
$ javac Main.java
── what you'll see ──
Main.java:6: error: variable s might not have been initialized
System.out.println(s);
^
1 error

Break #3

A stray semicolon after the condition

Put a ; right after if (x > 10).

Main.javawhole filejava
public class Main {
    public static void main(String[] args) {
        int x = 5;
        if (x > 10);
        {
            System.out.println("big");
        }
    }
}
terminal
$ java Main.java
── what you'll see ──
big

Myth vs fact

Myth

Indentation decides which statements belong to an if.

Fact

Only braces do. Without braces an if controls exactly one statement, and an else pairs with the nearest unmatched if.

Myth

if (x) works for numbers, like in C or JavaScript.

Fact

Java conditions must be boolean. if (count) doesn't compile; write if (count != 0).

Myth

Every condition in an else-if ladder is checked.

Fact

Java stops at the first true condition. Later rungs are never evaluated, which is why their order matters.

Myth

A long else-if chain is slow.

Fact

Each rung is one comparison and one jump. A ladder of five is a handful of nanoseconds. Choose if vs switch for readability, not speed.

Pro corner

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

  • ▸

    javac compiles if (a >= b) to the inverse jump (if_icmplt to the else-branch) so the then-branch falls through. The JIT later reorders basic blocks using branch profiles, so the hot path ends up as straight-line code regardless of how you wrote it.

  • ▸

    Definite assignment is specified in JLS chapter 16. It is flow analysis on the source, not on values: if (x > 2) s = "a"; if (x <= 2) s = "b"; still fails, because the compiler doesn't reason that the two conditions cover every case. A constant condition is different: if (true) s = "a"; passes, because constant expressions are evaluated at compile time.

  • ▸

    if (false) { ... } is deliberately not an 'unreachable statement' error (unlike while (false)), so you can write if (DEBUG) with a static final boolean DEBUG = false;. The compiler drops the block entirely from the bytecode: Java's form of conditional compilation.

  • ▸

    Branch-heavy code on unpredictable data can be slow on modern CPUs because of branch misprediction (around 10–20 cycles each). The JIT sometimes converts simple ifs into branch-free conditional moves (cmov). Measure with JMH before hand-optimising.

Remember this

  1. 1

    An if statement has a condition in round brackets and a block in curly braces: if (age >= 18) { ... }. The condition must be an expression of type boolean (true or false). Java does not treat numbers as truth values the way C does: if (1) and if (count) are compile errors, which rules out a whole family of C bugs.

  2. 2

    else gives the 'otherwise' path. else if lets you test more conditions in a row, forming a ladder. Java checks the conditions from top to bottom and runs only the first branch whose condition is true; the rest are skipped, even if they would also be true. That means the order of the tests matters: put the most specific (narrowest) condition first.

  3. 3

    Braces are optional when a branch has a single statement, but then only that one statement belongs to the if. Anything after it runs every time. An else always pairs with the nearest unmatched if (the 'dangling else' rule), whatever the indentation suggests. Always using braces removes both traps.

  4. 4

    The compiler tracks definite assignment: if you declare a variable and give it a value only inside some branches, Java refuses to let you read it afterwards (variable s might not have been initialized). Assign it in every branch (an else is often the missing piece) or give it a starting value.

  5. 5

    Conditions are often built with &&, || and ! from Topic 1.8. Because && and || short-circuit (they stop as soon as the answer is known), you can write a guard: if (name != null && name.length() > 3) never calls length() on null.

  6. 6

    Under the hood, an if becomes a conditional jump in bytecode. The compiler usually flips the test: if (age >= 18) compiles to 'if age < 18, jump over the then-block'. There is no runtime cost to else if beyond one comparison per rung that is actually tested.

Explain it without notes

01

What happens when two conditions in an else-if ladder are both true?

02

Explain the dangling else problem and how to avoid it.

03

Why does if (x = 5) fail to compile in Java when it compiles in C, and which variant of the typo still compiles?

04

What is definite assignment, and why does the compiler reject reading a variable assigned only inside an if?

Practice

01

Write a program that classifies a temperature t as freezing (below 0), cold (0–14), mild (15–24) or hot (25 and above). Test it with -5, 0, 20 and 30.

02

Given three ints a, b, c, print the largest using only if/else (no Math.max). Test with 3, 9, 4.

03

Print whether a number is positive, negative or zero, and on a second line whether it is even or odd. Test with -7 and 0.

Trade-offs

  • ↔

    An else-if ladder handles ranges and mixed conditions; a switch (Topics 2.3 and 2.4) reads better when you compare one value against many fixed constants.

  • ↔

    Guard clauses keep code flat, but many early exits can hide the main path in long methods. Use them for genuine special cases (bad input, nothing to do), not for every branch.

  • ↔

    Omitting braces saves two characters and invites the dangling-else and 'second line always runs' bugs. Most teams require braces always.

Done when you can

  • Done when you can write if, if/else and an else-if ladder from memory, with braces.

  • Done when you can explain why the order of an else-if ladder matters and fix a wrongly ordered one.

  • Done when you can spot the dangling else and the stray-semicolon bug in someone else's code.

  • Done when you can read the 'might not have been initialized' error and fix it by covering every path.

  • Done when you can explain that an if compiles to a conditional jump, and why the test appears inverted in bytecode.