Java flow control practice — validator, menu and guessing game

There’s a very specific moment in the FP2 lab that almost everyone goes through the same way. You’ve spent half an hour on a program that asks for several pieces of input, everything compiles, everything looks fine… and suddenly the program skips a question. No error. It just carries on as if you had already answered something. That’s when the hunt starts, checking line by line something that looks perfectly correct at first glance.

It’s the Scanner buffer trap, and you’re going to trigger it yourself in the first program of this practice, on purpose, so you recognise it instantly the next time it shows up.

Open VS Code, create a folder flow_control_practice and one .java file per program.

Java flow control practice — Program 1: User data validator

This program asks for age, email and password, and validates each one with if/else. It’s the perfect place to see the buffer trap in action, because it mixes nextInt() with nextLine() exactly like a real form would.

import java.util.Scanner;

public class UserValidator {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.println("=== USER REGISTRATION ===");
        System.out.println();

        // Age — nextInt() leaves the newline "\n" unconsumed
        System.out.print("Age: ");
        int age = sc.nextInt();
        sc.nextLine();    // cleaner — without this, email would read empty

        boolean validAge = age >= 16 && age <= 99;
        if (validAge) {
            System.out.println("✓ Valid age");
        } else {
            System.out.println("✗ Age must be between 16 and 99");
        }

        // Email — basic validation without external libraries
        System.out.print("Email: ");
        String email = sc.nextLine();

        // contains() looks for a piece of text inside the String
        boolean validEmail = email.contains("@") && email.contains(".");
        if (validEmail) {
            System.out.println("✓ Valid email format");
        } else {
            System.out.println("✗ Email must contain @ and a dot");
        }

        // Password — checking several conditions at once
        System.out.print("Password: ");
        String password = sc.nextLine();

        boolean lengthOk = password.length() >= 8;
        boolean hasDigit = false;
        for (char c : password.toCharArray()) {
            if (Character.isDigit(c)) {
                hasDigit = true;
                break;    // once we find a digit, no need to keep checking
            }
        }

        if (lengthOk && hasDigit) {
            System.out.println("✓ Valid password");
        } else if (!lengthOk && !hasDigit) {
            System.out.println("✗ Password too short and has no digits");
        } else if (!lengthOk) {
            System.out.println("✗ Password needs at least 8 characters");
        } else {
            System.out.println("✗ Password needs at least one digit");
        }

        // Final summary with ternary operator
        System.out.println();
        boolean allValid = validAge && validEmail && lengthOk && hasDigit;
        System.out.println(allValid ? "=== REGISTRATION COMPLETE ===" : "=== CHECK THE MARKED FIELDS ===");

        sc.close();
    }
}

Run the program and enter an age, say 20, and press Enter. Watch what happens with the email:

Age: 20
✓ Valid age
Email: ✗ Email must contain @ and a dot

See what happened? The program didn’t wait for you to type the email. nextInt() read the 20 but left the newline in the buffer, and nextLine() consumed it instead of waiting for anything else, running the validation on an empty email. Comment out the sc.nextLine(); line right after nextInt() and check for yourself that the bug comes back.

Whenever you use nextInt(), nextDouble() or nextLong() and then need to read text with nextLine(), add an empty sc.nextLine() in between. Without it, that nextLine() will swallow the leftover newline instead of waiting for you to type something.

Java flow control practice — Program 2: Interactive menu with switch

A menu is the most natural example for practising switch, because each numbered option maps perfectly onto a case. Here you’ll build one with several operations on a shopping list kept in memory while the program runs.

import java.util.Scanner;

public class ShoppingListMenu {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // Fixed-size array to keep things simple — 10 slots max
        String[] list = new String[10];
        int count = 0;    // how many products are actually stored

        int option;
        do {
            System.out.println("\n=== SHOPPING LIST ===");
            System.out.println("1. Add product");
            System.out.println("2. View full list");
            System.out.println("3. Search product");
            System.out.println("4. Clear list");
            System.out.println("5. Quit");
            System.out.print("Choose an option: ");
            option = sc.nextInt();
            sc.nextLine();    // cleaner — needed before the next nextLine()

            switch (option) {
                case 1:
                    if (count < list.length) {
                        System.out.print("Product to add: ");
                        list[count] = sc.nextLine();
                        count++;
                        System.out.println("✓ Product added");
                    } else {
                        System.out.println("✗ The list is full");
                    }
                    break;

                case 2:
                    if (count == 0) {
                        System.out.println("The list is empty");
                    } else {
                        System.out.println("--- Products ---");
                        for (int i = 0; i < count; i++) {
                            System.out.println((i + 1) + ". " + list[i]);
                        }
                    }
                    break;

                case 3:
                    System.out.print("Which product are you looking for? ");
                    String search = sc.nextLine();
                    boolean found = false;
                    for (int i = 0; i < count; i++) {
                        // equalsIgnoreCase compares without caring about case
                        if (list[i].equalsIgnoreCase(search)) {
                            found = true;
                            break;
                        }
                    }
                    System.out.println(found ? "✓ Yes, it's on the list" : "✗ Not on the list");
                    break;

                case 4:
                    count = 0;    // no need to erase every slot, just "forget" how many there are
                    System.out.println("✓ List cleared");
                    break;

                case 5:
                    System.out.println("Goodbye");
                    break;

                default:
                    System.out.println("✗ Invalid option, choose between 1 and 5");
            }
        } while (option != 5);

        sc.close();
    }
}

Look at case 4: clearing the list doesn’t require erasing every slot in the array one by one. Setting count = 0 is enough, because the rest of the program only looks at positions 0 through count, so whatever is left beyond that is simply ignored.

Try removing the break; from case 1 and see what happens when you add a product. You’ll see fall-through live: the program keeps executing case 2 right after, even though you only asked to add something. Put the break; back once you’ve checked it.

Java flow control practice — Program 3: Guess the number with hints

This last program combines everything: while to repeat until you guess right, if/else to give hints, and an attempt counter with a final rating based on how quickly you got it.

import java.util.Scanner;
import java.util.Random;

public class GuessTheNumber {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Random random = new Random();

        System.out.println("=== GUESS THE NUMBER ===");
        System.out.println("I'm thinking of a number between 1 and 100");
        System.out.println();

        // nextInt(100) generates a number between 0 and 99 — add 1 for the 1-100 range
        int secret = random.nextInt(100) + 1;
        int attempts = 0;
        int guess = -1;    // impossible value to force the first pass through while

        while (guess != secret) {
            System.out.print("Your guess: ");
            guess = sc.nextInt();
            attempts++;

            if (guess == secret) {
                System.out.println("You got it!");
            } else if (guess < secret) {
                // Give more or less of a hint depending on how far off you are
                int difference = secret - guess;
                System.out.println(difference > 15 ? "Much higher" : "A bit higher");
            } else {
                int difference = guess - secret;
                System.out.println(difference > 15 ? "Much lower" : "A bit lower");
            }
        }

        // Rating based on attempts used
        System.out.println();
        String rating;
        switch (attempts <= 10 ? Math.min(attempts, 10) / 3 : 4) {
            case 0:
            case 1:
                rating = "Sixth sense";
                break;
            case 2:
                rating = "Very good";
                break;
            case 3:
                rating = "Not bad";
                break;
            default:
                rating = "At least you got there";
        }

        System.out.printf("You got it in %d attempts — %s%n", attempts, rating);

        sc.close();
    }
}

Random.nextInt(n) always gives a number between 0 (inclusive) and n (exclusive). It’s the same pattern you already saw with range() in Python: if you need the range to start at 1, add 1 to the result, just like we did here with random.nextInt(100) + 1.

Visualise with Python Tutor

Select Java from the dropdown and paste in pythontutor.com:

import java.util.Scanner;

public class Demo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int age = 20;
        boolean validAge = age >= 16 && age <= 99;

        int option = 2;
        switch (option) {
            case 1:
                System.out.println("Add");
                break;
            case 2:
                System.out.println("View list");
                break;
            default:
                System.out.println("Other option");
        }

        int guess = 0;
        int secret = 42;
        while (guess != secret) {
            guess = secret;    // simulate guessing right on the first try
        }
        System.out.println("Guessed");
    }
}

Step through and watch two moments. When you reach the switch, Python Tutor jumps straight to case 2 without going through case 1 — it doesn’t evaluate the options in order like an if/else, it compares the value once and goes directly to its spot. And in the while, notice how the condition guess != secret is checked before each pass: once guess becomes equal to secret, the loop doesn’t run again.

Summary and next step

In this practice you combined Scanner, if/else and switch in three programs that resemble things you’ll actually code: validating input, managing a menu and controlling a loop with a changing condition. The buffer trap shouldn’t catch you off guard anymore — the moment you see a program “skip” a question, you already know where to look.

In the next article you’ll find exercises to solve on your own.

Next step: exercisesJava flow control exercises — validation, menus and loops

Sigue aprendiendo

One Comment

Leave a Reply

Your email address will not be published. Required fields are marked *