Control Flow

Reviewed & published by Brayan K

So far, every line of your code runs from top to bottom, every single time. Control flow changes that — it lets your program make decisions. Think of it like a traffic intersection: without signals, every car goes straight. With a traffic light, cars stop or go depending on the signal. That's what if, else, and switch do for your code.

Part of the free Java course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn

📋 Before You Start

This lesson builds on what you learned in Lessons 1-3. You should already know how to:

If any of that is fuzzy, go back to Operators for a quick review.

1️⃣ The Basic If Statement — "Do This Only If..."

The if statement is the simplest decision-maker. It checks a condition (something that's either true or false), and if it's true, it runs the code inside the curly braces. If it's false, it skips that code entirely.

Here's the pattern you'll use thousands of times:

if (condition) {
    // This code ONLY runs when condition is true
}
// Code here runs no matter what

// Real example:
int temperature = 35;
if (temperature > 30) {
    System.out.println("It's hot outside! ☀️");
}
// Output: It's hot outside! ☀️

int temperature2 = 15;
if (temperature2 > 30) {
    System.out.println("It's hot outside!");
}
// Output: (nothing — the condition was false, so it was skipped)

🔑 Key point: The condition inside the parentheses () must evaluate to true or false. This is why you learned comparison operators (>, <, ==) in the last lesson — they produce boolean values that if can use.

2️⃣ If-Else — "Do This, Otherwise Do That"

A plain if only handles the "yes" case. But what if you want to do something different when the condition is false? That's where else comes in. It's like saying "if this is true, do A; otherwise, do B."

int age = 20;

if (age >= 18) {
    System.out.println("You are an adult. You can vote! 🗳️");
} else {
    System.out.println("You are a minor. You can't vote yet.");
}
// Output: You are an adult. You can vote! 🗳️

// Try changing age to 15 — now the else block runs instead

Exactly one block always runs. If the condition is true, the if block runs and the else block is skipped. If the condition is false, the if block is skipped and the else block runs. There's no scenario where both run, or neither runs.

3️⃣ Else If — Multiple Conditions in Order

What if you have more than two possibilities? For example, a grade calculator needs to check A, B, C, D, and F — that's five options, not just two. Use else if to chain conditions together:

int score = 85;
String grade;

if (score >= 90) {
    grade = "A";        // 90-100
} else if (score >= 80) {
    grade = "B";        // 80-89
} else if (score >= 70) {
    grade = "C";        // 70-79
} else if (score >= 60) {
    grade = "D";        // 60-69
} else {
    grade = "F";        // Below 60
}

System.out.println("Score: " + score + " → Grade: " + grade);
// Output: Score: 85 → Grade: B

⚠️ Order matters! Java checks conditions from top to bottom and runs the first match. Once a match is found, ALL remaining else-if/else blocks are skipped. If you put score >= 60 first, a score of 95 would get "D" because 95 >= 60 is also true!

Rule of thumb: Put the most specific (or highest) condition first, then work your way down to the most general. The final else acts as a "catch everything else" safety net.

public class Main {
    public static void main(String[] args) {
        int[] scores = {95, 85, 72, 63, 45};

        for (int score : scores) {
            String grade;
            if (score >= 90) {
                grade = "A";        // 90-100: Excellent
            } else if (score >= 80) {
                grade = "B";        // 80-89: Good
            } else if (score >= 70) {
                grade = "C";        // 70-79: Average
            } else if (score >= 60) {
                grade = "D";        // 60-69: Below average
            } else {
                grade = "F";        // Below 60: Fail
            }
            System.out.println("Score " + score + " -> Grade " + grade);
        }
    }
}

4️⃣ Nested If — Decisions Inside Decisions

Sometimes you need to check a condition, and then check another condition inside that. This is called nesting. Think of it like a security checkpoint: first, check if you have a ticket. If yes, then check if it's VIP or regular.

boolean hasTicket = true;
boolean isVIP = true;

if (hasTicket) {
    System.out.println("Welcome to the event!");
    
    if (isVIP) {
        System.out.println("Please proceed to the VIP area. 🥂");
    } else {
        System.out.println("Please take a seat in the general area.");
    }
} else {
    System.out.println("Sorry, you need a ticket to enter. 🚫");
}

⚠️ Don't nest too deep! If you have 3+ levels of nesting, your code becomes very hard to read. This is called "pyramid of doom" or "arrow code." Instead, use early returns or combine conditions with &&:

// Instead of nesting:
if (hasTicket && isVIP) {
    System.out.println("VIP access granted");
}

5️⃣ Switch — Comparing One Value Against Many Options

When you're comparing one variable against many exact values, a switch statement is cleaner than a long if-else-if chain. It's like a vending machine — you press a button (A1, A2, B1), and the machine gives you the matching item.

String day = "Wednesday";

switch (day) {
    case "Monday":
        System.out.println("Back to work! 💼");
        break;
    case "Tuesday":
    case "Wednesday":
    case "Thursday":
        System.out.println("Midweek grind 💪");
        break;
    case "Friday":
        System.out.println("TGIF! 🎉");
        break;
    case "Saturday":
    case "Sunday":
        System.out.println("Weekend! 🏖️");
        break;
    default:
        System.out.println("Not a valid day");
}
// Output: Midweek grind 💪

How it works step by step:

6️⃣ The Break Trap — Fall-Through Explained

The #1 bug beginners make with switch is forgetting break. Without it, Java doesn't stop at the matching case — it "falls through" and runs ALL the cases below it too!

// ❌ BUG: Missing break statements!
int num = 1;
switch (num) {
    case 1:
        System.out.println("One");
        // No break! Falls through to case 2...
    case 2:
        System.out.println("Two");
        // No break! Falls through to case 3...
    case 3:
        System.out.println("Three");
}
// Output: One  Two  Three  (ALL three print!)

// ✅ FIXED: Add break after each case
switch (num) {
    case 1:
        System.out.println("One");
        break;   // ← Stops here!
    case 2:
        System.out.println("Two");
        break;
    case 3:
        System.out.println("Three");
        break;
}
// Output: One  (only the matching case)

💡 Java 14+ Enhanced Switch: Modern Java has a new switch syntax that eliminates the break problem entirely:

String result = switch (day) {
    case "Monday" -> "Start of week";
    case "Friday" -> "TGIF!";
    case "Saturday", "Sunday" -> "Weekend!";
    default -> "Midweek";
};

The arrow -> syntax doesn't need break and can return a value directly.

public class Main {
    public static void main(String[] args) {
        String[] days = {"Monday", "Tuesday", "Wednesday", "Thursday",
                         "Friday", "Saturday", "Sunday"};

        for (String day : days) {
            String plan;
            switch (day) {
                case "Monday":
                    plan = "Team meeting at 9am";
                    break;
                case "Tuesday":
                    plan = "Code review session";
                    break;
                case "Wednesday":
                    plan = "Lunch & learn";
                    break;
                case "Thursday":
                    plan = "Sprint planning";
                    break;
                case "Friday":
                    plan = "Demo day + early finish!";
                    break;
                case "Saturday":
                case "Sunday":
                    plan = "REST! (No coding today)";
                    break;
                default:
                    plan = "Unknown day";
            }
            System.out.println(day + ": " + plan);
        }

        // Fall-through demo: without 'break', execution continues
        // into the cases below the matching one.
        System.out.println("\n--- Fall-Through Demo ---");
        int num = 2;
        System.out.print("switch(" + num + ") without breaks -> ");
        StringBuilder out = new StringBuilder();
        switch (num) {
            case 1: out.append("One ");
            case 2: out.append("Two ");
            case 3: out.append("Three ");
            case 4: out.append("Four ");
        }
        System.out.println(out.toString().trim());
    }
}

7️⃣ The Ternary Operator — One-Line If/Else

Sometimes you just need to pick between two values based on a simple condition. Writing a full if/else for that feels like overkill. The ternary operator is a one-line shortcut:

// Syntax: condition ? valueIfTrue : valueIfFalse

// Instead of this:
String status;
if (age >= 18) {
    status = "Adult";
} else {
    status = "Minor";
}

// You can write this:
String status = (age >= 18) ? "Adult" : "Minor";

// More examples:
int max = (a > b) ? a : b;                    // Find the bigger number
String label = (count == 1) ? "item" : "items"; // Pluralize
double price = isMember ? 9.99 : 14.99;        // Membership discount

⚠️ Don't overuse it! The ternary operator is great for simple assignments. But if your logic is complex, use a regular if/else — it's much easier to read and debug. If you're nesting ternaries inside ternaries, that's a code smell.

8️⃣ When to Use If vs Switch vs Ternary

Each control flow tool has its sweet spot. Using the right one makes your code cleaner and easier to read:

ToolBest WhenReal-World Example
if / else2-3 conditions, especially with rangesAge verification, input validation
else if chainMultiple range-based conditionsGrade calculator, tax brackets, pricing tiers
switchOne variable, many exact valuesMenu options, day of week, command parser
ternarySimple A-or-B value assignmentMax value, plural label, discount price

9️⃣ Real-World Example: Login System

Let's combine everything you've learned into a realistic example. Here's a login system that checks multiple conditions:

String username = "admin";
String password = "secret123";
boolean isAccountLocked = false;
int failedAttempts = 0;

// Step 1: Check if account is locked
if (isAccountLocked) {
    System.out.println("🔒 Account is locked. Contact support.");

// Step 2: Check credentials
} else if (username.equals("admin") && password.equals("secret123")) {
    System.out.println("✅ Login successful! Welcome, " + username);
    failedAttempts = 0;  // Reset on success

// Step 3: Wrong password (right username)
} else if (username.equals("admin")) {
    failedAttempts++;
    System.out.println("❌ Wrong password! Attempts: " + failedAttempts);
    
    if (failedAttempts >= 3) {
        isAccountLocked = true;
        System.out.println("🔒 Too many attempts. Account locked.");
    }

// Step 4: Unknown user
} else {
    System.out.println("❌ User '" + username + "' not found.");
}

Notice how this uses nested if (the failed attempts check inside the else-if), logical AND (&&), and String comparison with .equals(). This is the kind of real code you'll write in actual applications.

public class Main {
    record Customer(String name, int age, boolean student, String day) {}

    static String getTicketPrice(int age, boolean isStudent, String dayOfWeek) {
        // Step 1: Free for toddlers
        if (age < 3) {
            return "$0.00 — Free (toddler)";
        }

        // Step 2: Determine base price by age (else-if chain)
        double basePrice;
        if (age <= 12) {
            basePrice = 8;     // Child
        } else if (age <= 64) {
            basePrice = 15;    // Adult
        } else {
            basePrice = 10;    // Senior
        }

        // Step 3: Student discount (ternary operator!)
        double discount = isStudent ? 0.20 : 0;

        // Step 4: Weekend surcharge (switch!)
        int surcharge;
        switch (dayOfWeek) {
            case "Saturday":
            case "Sunday":
                surcharge = 3;
                break;
            case "Friday":
                surcharge = 2;
                break;
            default:
                surcharge = 0;
        }

        double finalPrice = basePrice * (1 - discount) + surcharge;
        String label = age <= 12 ? "Child" : age <= 64 ? "Adult" : "Senior";
        if (isStudent) label += " (Student -20%)";
        if (surcharge > 0) label += " (+" + surcharge + " weekend)";

        return String.format("$%.2f — %s", finalPrice, label);
    }

    public static void main(String[] args) {
        System.out.println("=== Movie Ticket Pricing ===\n");

        Customer[] customers = {
            new Customer("Baby Emma", 2,  false, "Saturday"),
            new Customer("Tommy",     10, false, "Tuesday"),
            new Customer("Sarah",     22, true,  "Wednesday"),
            new Customer("Mike",      35, false, "Saturday"),
            new Customer("Prof. Liu", 68, false, "Friday")
        };

        for (Customer c : customers) {
            System.out.println("  " + c.name() + " (age " + c.age() + ", " + c.day() + "):");
            System.out.println("    -> " + getTicketPrice(c.age(), c.student(), c.day()));
        }
    }
}
public class Main {
    public static void main(String[] args) {
        // 🎯 YOUR TURN — replace each ___ using the hint beside it.

        int    daysLate   = 9;
        String membership = "student";

        // 1) A chain of bands. The second test only runs if the first failed.
        String band;
        if (daysLate == 0) {
            band = "on time";
        } ___ (daysLate <= 7) {              // 👉 replace ___ with else if
            band = "grace week";
        } else if (daysLate <= 30) {
            band = "standard fine";
        } else {
            band = "referred to the office";
        }
        System.out.println("Band: " + band);

        // 2) One value, several exact matches — that is a switch.
        double perDay;
        ___ (membership) {                   // 👉 replace ___ with switch
            case "student":
                perDay = 0.10;
                // 3) Without this, Java falls through into the next case.
                ___;                         // 👉 replace ___ with break
            case "staff":
                perDay = 0.05;
                break;
            default:
                perDay = 0.20;
        }
        System.out.printf("Rate: %.2f per day%n", perDay);

        // 4) The first seven days are free: pick one of two values inline.
        int chargeableDays = daysLate <= 7 ___ 0 : daysLate - 7;  // 👉 replace ___ with ?
        double fine = chargeableDays * perDay;
        System.out.println("Chargeable days: " + chargeableDays);
        System.out.printf("Fine: %.2f%n", fine);

        // 5) Compare for equality — a single = would assign, not test.
        String verdict = fine ___ 0 ? "nothing to pay" : "please pay at the desk";  // 👉 replace ___ with ==
        System.out.println(verdict);
    }
}

Common Mistakes Beginners Make

if (x = 5)   // ❌ This ASSIGNS 5 to x (won't compile for int)
if (x == 5)  // ✅ This COMPARES x to 5
if (name == "Alice")       // ❌ Compares memory addresses
if (name.equals("Alice"))  // ✅ Compares actual text content
if (x > 5);  // ❌ This semicolon makes the if do NOTHING
{
    System.out.println("This always runs!");
}

Pro Tips

💡 Use early returns to avoid deep nesting. Instead of wrapping everything in if-else, check for the "bad" case first and return early.

💡 Always include default in switch statements — it catches unexpected values and makes debugging easier.

💡 Use constants or enums for switch values instead of "magic strings" — prevents typos and enables auto-complete.

💡 Combine conditions with && and || instead of deeply nesting if statements.

💡 Keep conditions simple — if a condition is complex, extract it into a boolean variable with a descriptive name:

// Hard to read:
if (age >= 18 && hasLicense && !isSuspended && insuranceValid) { ... }

// Easier to read:
boolean canDrive = age >= 18 && hasLicense && !isSuspended && insuranceValid;
if (canDrive) { ... }

📋 Quick Reference

StatementSyntaxWhen to Use
ifif (cond) { }Single yes/no check
if-elseif (cond) { } else { }Two possible paths
else ifif...else if...elseMultiple range-based decisions
switchswitch (val) { case: break; }One variable, many exact values
switch (new)case "x" -> resultJava 14+ — no break needed
ternarycond ? a : bSimple inline value pick

🎉 Lesson Complete!

Your programs can now make intelligent decisions! You've learned if-else chains, switch statements, the ternary operator, nested conditions, and when to use each one. You even built a real-world ticket pricing system!

Practice quiz

What must the condition inside an if statement's parentheses evaluate to?

  • A number
  • A String
  • true or false (boolean)
  • An array

Answer: true or false (boolean). An if condition must be a boolean — it evaluates to either true or false.

In an if-else, how many of the two blocks run?

  • Exactly one always runs
  • Both always run
  • Neither runs
  • It depends on the loop

Answer: Exactly one always runs. Exactly one block runs: the if block if the condition is true, otherwise the else block.

In an else-if chain, what happens after the first matching condition runs?

  • All remaining blocks also run
  • The program crashes
  • It loops back to the top
  • The remaining else-if/else blocks are skipped

Answer: The remaining else-if/else blocks are skipped. Java runs the first matching block, then skips all remaining else-if and else blocks.

In a switch statement, what happens if you forget break in a matching case?

  • A compile error
  • Execution falls through into the cases below
  • Nothing runs
  • Only that case runs

Answer: Execution falls through into the cases below. Without break, execution 'falls through' and runs the following cases too.

What does this print? int num=2; switch(num){ case 1: System.out.print("One "); case 2: System.out.print("Two "); case 3: System.out.print("Three "); }

  • Two Three
  • Two
  • One Two Three
  • Three

Answer: Two Three. Matching starts at case 2 and, with no break, falls through to case 3, printing 'Two Three '.

What is the ternary operator's syntax?

  • if ? then : else
  • condition : true ? false
  • condition ? valueIfTrue : valueIfFalse
  • value ? value ? value

Answer: condition ? valueIfTrue : valueIfFalse. The ternary form is condition ? valueIfTrue : valueIfFalse — a one-line if/else.

Which tool is best for comparing one variable against many exact values?

  • A for loop
  • A switch statement
  • A ternary operator
  • A nested if only

Answer: A switch statement. A switch is the cleanest way to compare one variable against many exact values.

What does String status = (age >= 18) ? "Adult" : "Minor"; assign when age is 15?

  • "Adult"
  • null
  • An error
  • "Minor"

Answer: "Minor". 15 >= 18 is false, so the ternary returns the false branch, "Minor".

What is wrong with: if (x > 5); { System.out.println("hi"); }

  • Nothing
  • The stray semicolon makes the if do nothing; the block always runs
  • It will not compile
  • It prints twice

Answer: The stray semicolon makes the if do nothing; the block always runs. The semicolon ends the if with an empty body, so the following block always runs regardless of x.

In the enhanced (arrow) switch like case "x" -> result, do you need break?

  • Yes, always
  • Only for the default
  • No, the arrow form does not fall through
  • Only for Strings

Answer: No, the arrow form does not fall through. The Java 14+ arrow syntax doesn't fall through, so no break is needed.

Continue this course