Saltar a contenido

03 - Decisions and Loops

What this session is

About an hour. You'll learn if/else/else if, the three loop forms (for, enhanced for, while), and modern Java's switch expressions - a much-improved version of the old switch statement.

Decisions: if / else

The world's smallest decision:

public class Age {
    public static void main(String[] args) {
        int age = 18;
        if (age >= 18) {
            System.out.println("adult");
        } else {
            System.out.println("minor");
        }
    }
}

What's new: - if (condition) { ... } - the parentheses are required. Code in braces runs only when the condition is true. - else { ... } - runs when the condition is false.

Java requires the braces. Some languages let you skip them for single-line bodies; Java doesn't, and that rule prevents a class of bugs. Always braces.

Comparison operators

Operator Meaning
== equal to
!= not equal to
< less than
<= less than or equal to
> greater than
>= greater than or equal to

Important - strings: == compares references, not contents. To compare string contents, use .equals:

String a = "hello";
String b = "hello";
if (a == b) { ... }            // might be true (or might not - undefined)
if (a.equals(b)) { ... }       // always correct for content comparison

Always use .equals for strings. The "use == for primitives, .equals for objects" rule is one of the most-told Java beginner rules.

Chaining: else if

int score = 75;
if (score >= 90) {
    System.out.println("A");
} else if (score >= 80) {
    System.out.println("B");
} else if (score >= 70) {
    System.out.println("C");
} else {
    System.out.println("F");
}

First match wins, top to bottom. If none match, else runs.

Combining: &&, ||, !

Operator Meaning
&& and (both true)
\|\| or (at least one true)
! not (flip)
int age = 25;
boolean hasLicense = true;
if (age >= 18 && hasLicense) {
    System.out.println("can drive");
}

Short-circuit: && doesn't evaluate the right side if the left is false. || doesn't evaluate the right if the left is true. Useful when the right side is expensive or might fail:

if (obj != null && obj.isReady()) { ... }   // safe even if obj is null

Repetition 1: for

The C-style for:

for (int i = 1; i <= 5; i++) {
    System.out.println(i);
}

Three parts, separated by semicolons: 1. int i = 1 - runs once, before anything else. 2. i <= 5 - checked before each iteration; loop continues while true. 3. i++ - runs after each iteration. (Shorthand for i = i + 1.)

Output: 1, 2, 3, 4, 5.

The compact form. Useful when you need the index.

Repetition 2: enhanced for (for-each)

When iterating a collection or array, the enhanced form reads better:

String[] fruits = {"apple", "banana", "cherry"};
for (String fruit : fruits) {
    System.out.println(fruit);
}

Read for (T x : collection) as "for each x in collection." Use this whenever you don't need the index.

Repetition 3: while

int n = 10;
while (n > 0) {
    System.out.println(n);
    n = n - 1;
}

Keep going while the condition is true. The body must change something that affects the condition.

There's also do-while, which runs the body at least once before checking:

int x;
do {
    x = readInput();
} while (x < 0);

Rare; use when "do something once, then maybe repeat" is the natural shape.

Breaking out: break and continue

for (int i = 1; i <= 10; i++) {
    if (i == 5) break;        // stop the loop entirely
    if (i % 2 == 0) continue; // skip to next iteration
    System.out.println(i);
}

Output: 1, 3. (i=1 prints; i=2 even → skip; i=3 prints; i=4 even → skip; i=5 → break.)

Switch expressions: modern Java

Old switch (still works, has a famous fall-through bug):

switch (day) {
    case 1: System.out.println("Mon"); break;
    case 2: System.out.println("Tue"); break;
    case 3: System.out.println("Wed"); break;
    default: System.out.println("?");
}

The break is required - without it, execution "falls through" to the next case. Forgetting break is the canonical switch bug.

Modern Java (14+) has switch expressions with arrow syntax - no fall-through, no break required, returns a value:

int day = 2;
String name = switch (day) {
    case 1 -> "Mon";
    case 2 -> "Tue";
    case 3 -> "Wed";
    default -> "?";
};
System.out.println(name);    // Tue

What's new: - case 1 -> instead of case 1:. The arrow form doesn't fall through. - The whole switch is an expression - produces a value you can assign or pass. - The semicolon after the closing } (because the whole thing is one statement).

For multiple matching values:

String type = switch (day) {
    case 1, 2, 3, 4, 5 -> "weekday";
    case 6, 7 -> "weekend";
    default -> "invalid";
};

For a block body (when one expression isn't enough), use yield:

String result = switch (input) {
    case "a" -> "got a";
    case "b" -> {
        System.out.println("processing b");
        yield "got b";    // yield is "return from this case"
    }
    default -> "unknown";
};

Always use the modern arrow form in new code. We'll see it heavily in page 08 when we meet pattern matching.

Exercise

In a new file Classify.java:

Write the classic FizzBuzz. For each number from 1 to 20:

  • Divisible by 3 → print Fizz.
  • Divisible by 5 → print Buzz.
  • Divisible by both → print FizzBuzz.
  • Otherwise → print the number.

Hint: check "both 3 and 5" first. Why? Think about what would happen if you checked "divisible by 3" first.

Expected output starts:

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
...

Don't move on until your program prints exactly the right thing.

Stretch: rewrite using a switch expression on n % 15. (15 = 3 × 5 - what values of n % 15 mean "divisible by 3 only", "by 5 only", "by both"?)

What you might wonder

"Why are parens required around if conditions?" Java's grammar requires them. Python (if x > 0:) doesn't; Java does. Live with it.

"Why does == on strings sometimes work?" Java interns string literals - identical literals share the same object, so "hello" == "hello" is true. But "hello" == new String("hello") is false (different objects, same content). Always use .equals to avoid this trap.

"Switch expression vs switch statement - when does the old form make sense?" Almost never in new code. The arrow form is shorter, safer (no fall-through), and produces a value. The only reason to use the old form is to integrate with very old codebases.

"What about pattern matching?" Modern Java's switch can also match on types and destructure: case Point(int x, int y) -> .... We'll meet this in page 08 after we've learned classes.

Done

You can now: - Make a program take different actions with if/else if/else. - Use comparison and logical operators correctly. - Know that .equals compares string contents; == is for primitives. - Iterate with C-style for, enhanced for, and while. - Use break and continue. - Write modern switch expressions.

You have the core control flow. Next page: methods - Java's word for functions.

Next: Methods →

Comments