Lab 05: Control Flow & Recursion

Objective

Master Java control flow: if/else, switch expressions, for/while/do-while loops, break/continue, and labeled statements.

Background

Control flow determines which statements execute and in what order. Java 14+ introduced switch expressions (with -> arrows and yield) that are more concise and safer than traditional switch statements. Combined with enhanced for loops and text blocks, modern Java control flow is expressive and readable.

Time

35 minutes

Prerequisites

  • Lab 02 (Variables & Primitives)

  • Lab 04 (Arrays)

Tools

  • Java 21 (Eclipse Temurin)

  • Docker image: innozverse-java:latest


Lab Instructions

Step 1: If/Else and Ternary

💡 Pattern matching instanceof (Java 16+) eliminates the cast: instead of if (obj instanceof String) { String s = (String) obj; } you write if (obj instanceof String s). The && short-circuits, so s is safely bound only when the instanceof check passes.

📸 Verified Output:


Step 2: Switch Expressions (Modern Java)

💡 Switch expressions vs statements: The arrow -> form is an expression that returns a value, requires exhaustiveness (all cases covered), and can't fall through. No need for break. yield is used for multi-statement cases: case X -> { doStuff(); yield value; }.

📸 Verified Output:


Step 3: For, While, Do-While

💡 Use while when you don't know the iteration count upfront, do-while when the body must execute at least once (user input validation, retry loops). Prefer enhanced for-each over indexed loops when you don't need the index.

📸 Verified Output:


Step 4: Break, Continue & Labels

💡 Labeled break/continue let you exit or skip outer loops from inside inner ones. While rare in everyday code, they avoid boolean flag variables in nested search loops. The label must be on the loop statement, not just before it.

📸 Verified Output:


Step 5: Iteration Patterns — Practical Algorithms

💡 The sliding window pattern processes a fixed-size window of elements by adding the new element and removing the old one — O(n) instead of O(n×k) for recomputing each window. Two pointers is another O(n) pattern that replaces nested loops in many problems.

📸 Verified Output:


Step 6: Recursion

💡 Every recursive function needs a base case (stops recursion) and makes progress toward it. Without a base case you get StackOverflowError. The Fibonacci implementation here is exponential O(2^n) — in practice, memoize it or use dynamic programming.

📸 Verified Output:


Step 7: Text Blocks & Formatted Output

💡 Text blocks (triple-quoted strings) preserve indentation relative to the closing """. The formatted() method works like String.format() but chains nicely. %n is the platform-appropriate newline — prefer it over \n in printf.

📸 Verified Output:


Step 8: Putting It Together — Number Guessing Game Logic

💡 Binary search always finds a number in 1–100 within 7 guesses (log₂100 ≈ 6.6). This is why binary search is O(log n) — each guess halves the search space. The same logic applies to searching sorted arrays, database indexes, and Git's bisect.

📸 Verified Output:


Verification

Summary

You've mastered Java control flow: modern switch expressions, all loop types, break/continue with labels, recursion, and formatted output. These patterns underpin every Java program you'll ever write.

Further Reading

Last updated