Skip to content

VOL 01 / CH 04 / LESSON 05

4.5 Lab: Branches, Loops, and Boundaries ​

Prerequisites: All control flow constructs in this chapter Estimated time: 50 minutes Practice environment: JDK 17+ and Bash; all files written to a temporary directory You are ready to continue when: You have completed four required exercises and provide boundary cases for each

bash
chapter4_lab_root=$(mktemp -d)
readonly chapter4_lab_root
mkdir -p "$chapter4_lab_root/chapter04-lab/out"
cd "$chapter4_lab_root/chapter04-lab"

Required Exercise 1: Score Classification Boundaries ​

Save as GradeClassifier.java:

java
public class GradeClassifier {
    static String classify(int score) {
        if (score < 0 || score > 100) {
            return "invalid";
        } else if (score >= 90) {
            return "excellent";
        } else if (score >= 80) {
            return "good";
        } else if (score >= 60) {
            return "pass";
        }
        return "retry";
    }

    public static void main(String[] args) {
        int[] cases = {-1, 0, 59, 60, 79, 80, 89, 90, 100, 101};
        for (int score : cases) {
            System.out.println(score + ":" + classify(score));
        }
    }
}
bash
javac -d out GradeClassifier.java
java -cp out GradeClassifier

Carefully verify each boundary point, don’t just check whether the program exits successfully.

Required Exercise 2: Switch Expression ​

Save as ItemPrice.java:

java
public class ItemPrice {
    static int priceOf(String item) {
        if (item == null) {
            throw new IllegalArgumentException("item is null");
        }
        return switch (item) {
            case "book" -> 50;
            case "magazine" -> 40;
            case "notebook" -> 35;
            default -> throw new IllegalArgumentException("item: " + item);
        };
    }

    public static void main(String[] args) {
        System.out.println(priceOf("book"));
        System.out.println(priceOf("magazine"));
    }
}
bash
javac -d out ItemPrice.java
java -cp out ItemPrice

Then pass in "hammer" and null, recording the exception type and message. Unknown values should not silently default to a price of 0.

Required Exercise 3: A 1–9 Multiplication Table ​

Save as MultiplicationTable.java:

java
public class MultiplicationTable {
    public static void main(String[] args) {
        for (int row = 1; row <= 9; row++) {
            for (int column = 1; column <= row; column++) {
                System.out.print(column + "×" + row + "="
                        + (column * row) + "\t");
            }
            System.out.println();
        }
    }
}
bash
javac -d out MultiplicationTable.java
java -cp out MultiplicationTable

Verification: 9 rows in total; row 1 contains 1 item; row 9 contains 9 items; the final item is 9×9=81.

Required Exercise 4: Find the First Target ​

Save as GridSearch.java:

java
public class GridSearch {
    static int[] findFirst(int[][] grid, int target) {
        search:
        for (int row = 0; row < grid.length; row++) {
            for (int column = 0; column < grid[row].length; column++) {
                if (grid[row][column] == target) {
                    return new int[] {row, column};
                }
            }
        }
        return new int[] {-1, -1};
    }

    public static void main(String[] args) {
        int[][] grid = {{1, 2}, {3, 7}, {7, 9}};
        System.out.println(java.util.Arrays.toString(findFirst(grid, 7)));
        System.out.println(java.util.Arrays.toString(findFirst(grid, 8)));
    }
}
bash
javac -d out GridSearch.java
java -cp out GridSearch

Expect [1, 1] and [-1, -1]: the two integers are the row and column, and two -1 values mean no match. This example borrows array syntax from the next chapter to return a coordinate pair. Packing coordinates into row * 1000 + column would allow collisions when rows have many columns. The outer array and each row must be non-null; empty arrays and rows are allowed. Production code can return a result type with named fields.

The unused search: label is intentional: after extracting a method that returns the result, the label is redundant. Remove it.

Optional Exercise 1: Number Guessing That Can Stop ​

Separate the random number generation from user interaction to first test the deterministic core:

java
static String compareGuess(int target, int guess) {
    if (guess < target) {
        return "low";
    }
    if (guess > target) {
        return "high";
    }
    return "correct";
}

When testing target=50 with values 49, 50, and 51, remember that the actual interactive loop must handle non-numeric input, input termination, maximum attempt limits, and cancellation. Do not write while (true) first and then assume the user will always guess correctly.

Optional Exercise 2: Observe the Landing of continue ​

java
for (int value = 0; value < 5; value++) {
    if (value == 2) {
        continue;
    }
    System.out.println(value);
}

Rewrite it as an equivalent while, ensuring that state progression occurs before continue. If the program hangs, interrupt execution first, then inspect where variables are updated; do not let a stalled process consume the terminal session for an extended period.

Fault Experiment ​

Open one at a time:

java
// if (1) { }
// switch (1L) { default -> { } }
// for (int index = 0; index < 3; index++); { System.out.println("once"); }

The first two fail to compile; the third compiles but the block executes only once. Record the difference between "compile failure" and "semantic error".

Safe Cleanup ​

bash
pwd
test ! -e out || rm -r -- out

Ensure you're in the directory for this experiment and only remove artifacts produced by this experiment.

Acceptance and Cleanup ​

Verify each item: score thresholds, unknown items and null, the number of rows and endpoints in the multiplication table, the two search paths: a match and no match. In the failure experiment, separately document compilation errors, runtime exceptions, and cases where the program runs but behaves incorrectly. After saving your experiment notes, clean up the entire temporary directory:

bash
cd
ls -ld -- "$chapter4_lab_root"
rm -r -- "$chapter4_lab_root"

Classification, pricing, and search use different syntax, but each needs the same path checks: where execution enters, how state changes, where it exits, and who handles exceptions.

The next chapter will encapsulate this repeated logic.

→ Next: Methods and Call Stacks

Built with VitePress | Software Systems Atlas