Java flow control exercises — validation, menus and loops

In the practice you combined Scanner, if/else and switch in programs that looked like real things. Now it’s your turn to solve on your own. All three exercises circle back to the same idea you already saw: validating input, making decisions based on several conditions and controlling a loop that depends on what the user types.

Try to solve them before checking the solution. If you’re stuck for more than 10 minutes, use the hint.


Java flow control exercises — Basic Level

Exercise 1 — University entry grade validator

Write a program that asks for a university entry grade (0-14 in the Spanish system) and classifies the student based on the result. It should also say whether that grade gets into a degree with an entry cutoff of 10.5, a typical value for high-demand degrees like GCID.

Expected output with grade 11.8:

=== ENTRY GRADE VALIDATOR ===

Entry grade (0-14): 11.8

--- Result ---
Classification: Merit
Gets into a degree with a 10.5 cutoff? YES ✓
Margin above the cutoff: 1.30 points

Classification:

< 5           → Fail
5 - 6.9       → Pass
7 - 8.9       → Merit
9 - 10.9      → Very good
>= 11         → Outstanding

If the grade is outside 0-14, the program must warn that the input isn’t valid and stop, without calculating anything else.

Don’t calculate the classification before validating the range. If you validate first and cut with return when the input doesn’t make sense, the rest of the code can safely assume the grade is between 0 and 14 without checking again.

💡 Hints:

  • Validate the range with an if at the top; if it fails, stop right there with a message and don’t calculate anything else
  • Use a chained if/else if for the classification — these are ranges, not exact values, so switch doesn’t fit here
  • The margin above the cutoff is simply grade - 10.5, and it only makes sense to show it when the result is positive

Java flow control exercises — Intermediate Level

Exercise 2 — Library menu

Write a program that manages a small library with an array of up to 5 books. The menu must let you add a book, mark it as borrowed, return it and view the status of all of them.

Expected output for a full session:

=== LIBRARY ===
1. Add book
2. Borrow book
3. Return book
4. View all
5. Quit
Option: 1
Title: Clean Code
✓ Book added

1. Add book
2. Borrow book
3. Return book
4. View all
5. Quit
Option: 2
Title to borrow: Clean Code
✓ Borrowed

1. Add book
2. Borrow book
3. Return book
4. View all
5. Quit
Option: 4
--- Library status ---
1. Clean Code — Borrowed

1. Add book
2. Borrow book
3. Return book
4. View all
5. Quit
Option: 5
Goodbye

If you try to borrow a book that’s already borrowed, or one that doesn’t exist, the program must warn about it without breaking.

If you declare a variable inside a case without its own braces, and another case in the same switch declares a variable with the same name, Java throws a compile error — even though the two cases never run at the same time. That’s why case 2 and case 3 are wrapped in { } in the solution.

💡 Hints:

  • Keep two parallel arrays: String[] titles and boolean[] borrowed, both of size 5, using the same “counter” idea you used in the practice
  • To find a book by title, loop through the array with a for and compare with .equals(), never with ==
  • The main loop is a do-while, just like in the practice: the menu always shows at least once

Java flow control exercises — Final Challenge

Exercise 3 — ATM with PIN

Write a program that simulates an ATM: it first asks for a 4-digit PIN and gives a maximum of 3 attempts before locking the session. If the PIN is correct, it shows a menu to check balance, deposit money, withdraw money and quit, repeating until the user chooses to quit.

Expected output:

=== ATM ===

Enter your PIN: 1111
Wrong PIN (2 attempts left)
Enter your PIN: 2222
Wrong PIN (1 attempt left)
Enter your PIN: 4321
✓ Access granted

--- Menu ---
1. Check balance
2. Deposit money
3. Withdraw money
4. Quit
Option: 1
Current balance: 500.00 €

--- Menu ---
1. Check balance
2. Deposit money
3. Withdraw money
4. Quit
Option: 3
Amount to withdraw: 700
✗ Insufficient funds (available: 500.00 €)

--- Menu ---
1. Check balance
2. Deposit money
3. Withdraw money
4. Quit
Option: 4
Session ended

The correct PIN is 4321 and the starting balance is 500.0. If all 3 attempts run out without success, the program must end without showing the menu, with a card-locked message.

When a loop depends on several conditions joined with &&, like attempts > 0 && !granted, you don’t need to work out the combined result yourself. Java checks each part in order and, as soon as one is false, it doesn’t even look at the rest — so the order of your conditions can save work if you put the one most likely to fail first.

💡 Hints:

  • The PIN block is a while that repeats while the attempt is wrong and there are still attempts left — two conditions joined with &&
  • Use an attempts variable that starts at 3 and drops by 1 each time you fail, and check whether it hit 0 to decide whether to lock the card or let the user in
  • The operations menu is a do-while with switch, just like in Exercise 2 and in the practice

What’s wrong here?

Before the solutions, three common mistakes from this part of the syllabus. Can you spot the problem in each one?

Mistake 1

Scanner sc = new Scanner(System.in);
System.out.print("Grade: ");
double grade = sc.nextDouble();
System.out.print("Comment: ");
String comment = sc.nextLine();  // ← skips the question

The cleanup sc.nextLine() after nextDouble() is missing. Without it, the second nextLine() reads the leftover newline instead of waiting for your input:

double grade = sc.nextDouble();
sc.nextLine();  // ← consume the leftover \n
String comment = sc.nextLine();  // now it actually waits

Mistake 2

int option = 2;
switch (option) {
    case 1:
        System.out.println("One");
    case 2:
        System.out.println("Two");
    case 3:
        System.out.println("Three");
        break;
}
// Prints "Two" and "Three" — not just "Two"

The break statements are missing in cases 1 and 2, so the program falls through from case 2 to the first break it finds. The fix is to add break; at the end of every case, unless the fall-through is intentional:

case 2:
    System.out.println("Two");
    break;  // ← now it stops here

Mistake 3

String answer = sc.nextLine();
if (answer == "yes") {
    System.out.println("Confirmed");
}
// Never enters, even if you type exactly "yes"

== compares memory references for Strings, not their content. The correct comparison is always .equals():

if (answer.equals("yes")) {  // ← correct

Commented solutions

Solution Exercise 1

import java.util.Scanner;

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

        System.out.println("=== ENTRY GRADE VALIDATOR ===");
        System.out.println();

        System.out.print("Entry grade (0-14): ");
        double grade = sc.nextDouble();

        // Stop right here if the input doesn't make sense — no further calculation
        if (grade < 0 || grade > 14) {
            System.out.println("✗ Grade must be between 0 and 14");
            sc.close();
            return;
        }

        // Range-based classification — if/else if, not switch, since these are double ranges
        String classification;
        if (grade < 5) {
            classification = "Fail";
        } else if (grade < 7) {
            classification = "Pass";
        } else if (grade < 9) {
            classification = "Merit";
        } else if (grade < 11) {
            classification = "Very good";
        } else {
            classification = "Outstanding";
        }

        // Fixed cutoff for this exercise — in practice it would vary by degree
        double cutoff = 10.5;
        boolean admitted = grade >= cutoff;

        System.out.println();
        System.out.println("--- Result ---");
        System.out.printf("Classification: %s%n", classification);
        // The ternary operator avoids a full if/else for a single piece of text
        System.out.printf("Gets into a degree with a %.1f cutoff? %s%n",
                          cutoff, admitted ? "YES ✓" : "NO ✗");

        // The margin only makes sense to show if the student is actually admitted
        if (admitted) {
            System.out.printf("Margin above the cutoff: %.2f points%n", grade - cutoff);
        }

        sc.close();
    }
}

Solution Exercise 2

import java.util.Scanner;

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

        // Parallel arrays: position i in one matches position i in the other
        String[] titles   = new String[5];
        boolean[] borrowed = new boolean[5];
        int count = 0;    // how many books are actually stored

        int option;
        do {
            System.out.println("\n=== LIBRARY ===");
            System.out.println("1. Add book");
            System.out.println("2. Borrow book");
            System.out.println("3. Return book");
            System.out.println("4. View all");
            System.out.println("5. Quit");
            System.out.print("Option: ");
            option = sc.nextInt();
            sc.nextLine();    // cleaner — you know why by now

            switch (option) {
                case 1:
                    if (count < titles.length) {
                        System.out.print("Title: ");
                        titles[count] = sc.nextLine();
                        borrowed[count] = false;    // new book, available by default
                        count++;
                        System.out.println("✓ Book added");
                    } else {
                        System.out.println("✗ The library is full");
                    }
                    break;

                case 2: {
                    System.out.print("Title to borrow: ");
                    String search = sc.nextLine();
                    int index = -1;    // -1 means "not found yet"
                    for (int i = 0; i < count; i++) {
                        if (titles[i].equalsIgnoreCase(search)) {
                            index = i;
                            break;    // found it — no need to keep looking
                        }
                    }
                    if (index == -1) {
                        System.out.println("✗ That book doesn't exist");
                    } else if (borrowed[index]) {
                        System.out.println("✗ It's already borrowed");
                    } else {
                        borrowed[index] = true;
                        System.out.println("✓ Borrowed");
                    }
                    break;
                }

                case 3: {
                    System.out.print("Title to return: ");
                    String search = sc.nextLine();
                    int index = -1;
                    for (int i = 0; i < count; i++) {
                        if (titles[i].equalsIgnoreCase(search)) {
                            index = i;
                            break;
                        }
                    }
                    if (index == -1) {
                        System.out.println("✗ That book doesn't exist");
                    } else if (!borrowed[index]) {
                        System.out.println("✗ That book wasn't borrowed");
                    } else {
                        borrowed[index] = false;
                        System.out.println("✓ Returned");
                    }
                    break;
                }

                case 4:
                    if (count == 0) {
                        System.out.println("The library is empty");
                    } else {
                        System.out.println("--- Library status ---");
                        for (int i = 0; i < count; i++) {
                            String status = borrowed[i] ? "Borrowed" : "Available";
                            System.out.printf("%d. %s — %s%n", i + 1, titles[i], status);
                        }
                    }
                    break;

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

                default:
                    System.out.println("✗ Invalid option");
            }
        } while (option != 5);

        sc.close();
    }
}

Notice cases 2 and 3: they’re wrapped in their own { }, separate from the switch‘s. Without them, declaring int index in two different case blocks would cause a duplicate-variable error, because technically they’d still be in the same block. The braces give each case its own scope.

Solution Exercise 3

import java.util.Scanner;

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

        final String correctPin = "4321";
        double balance = 500.0;

        System.out.println("=== ATM ===");
        System.out.println();

        int attempts = 3;
        boolean granted = false;

        // Keep asking for the PIN while attempts remain AND access hasn't been granted
        while (attempts > 0 && !granted) {
            System.out.print("Enter your PIN: ");
            String pin = sc.nextLine();

            if (pin.equals(correctPin)) {
                granted = true;
                System.out.println("✓ Access granted");
            } else {
                attempts--;
                if (attempts > 0) {
                    System.out.printf("Wrong PIN (%d attempt%s left)%n",
                                      attempts, attempts == 1 ? "" : "s");
                }
            }
        }

        // If we exit the while without access, it's because attempts ran out
        if (!granted) {
            System.out.println("✗ Card locked — too many failed attempts");
            sc.close();
            return;    // ends the program — never reaches the menu
        }

        int option;
        do {
            System.out.println("\n--- Menu ---");
            System.out.println("1. Check balance");
            System.out.println("2. Deposit money");
            System.out.println("3. Withdraw money");
            System.out.println("4. Quit");
            System.out.print("Option: ");
            option = sc.nextInt();

            switch (option) {
                case 1:
                    System.out.printf("Current balance: %.2f €%n", balance);
                    break;

                case 2:
                    System.out.print("Amount to deposit: ");
                    double deposit = sc.nextDouble();
                    balance += deposit;
                    System.out.printf("✓ Deposit completed. Balance: %.2f €%n", balance);
                    break;

                case 3:
                    System.out.print("Amount to withdraw: ");
                    double withdrawal = sc.nextDouble();
                    if (withdrawal > balance) {
                        System.out.printf("✗ Insufficient funds (available: %.2f €)%n", balance);
                    } else {
                        balance -= withdrawal;
                        System.out.printf("✓ Withdrawal completed. Balance: %.2f €%n", balance);
                    }
                    break;

                case 4:
                    System.out.println("Session ended");
                    break;

                default:
                    System.out.println("✗ Invalid option");
            }
        } while (option != 4);

        sc.close();
    }
}

The return; right after locking the card is the key piece: it cuts the program off right there and stops execution from reaching the menu, exactly like you did with the range validation in Exercise 1.


Visualise with Python Tutor

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

public class Demo {
    public static void main(String[] args) {
        String correctPin = "4321";
        int attempts = 3;
        boolean granted = false;

        String[] tries = {"1111", "2222", "4321"};
        int i = 0;

        while (attempts > 0 && !granted && i < tries.length) {
            String pin = tries[i];
            if (pin.equals(correctPin)) {
                granted = true;
            } else {
                attempts--;
            }
            i++;
        }

        System.out.println("Granted: " + granted);
        System.out.println("Attempts left: " + attempts);
    }
}

Step through and watch the while condition: it has three parts joined with &&, and just one being false is enough to stop the loop. Notice how attempts drops from 3 to 2 to 1 with the first two failed PINs, and how on the third pass pin.equals(correctPin) is true, granted becomes true, and even though attempts (1) is still greater than 0, the loop ends because all three conditions are no longer true at once.


Cheat sheet — Java flow control

// ============================================
// CHEAT SHEET — Java Flow Control
// Sergio Learns · sergiolearns.com
// ============================================

// VALIDATE BEFORE MOVING ON — cut the flow early
if (value < min || value > max) {
    System.out.println("Invalid input");
    return;    // or break/continue depending on context
}
// the rest of the code can now assume the value is correct

// IF/ELSE IF — for ranges and complex conditions
if (x < 5) {
    ...
} else if (x < 10) {
    ...
} else {
    ...
}

// SWITCH — for exact values (int, char, String)
switch (variable) {
    case value1:
        // code
        break;              // required — prevents fall-through
    case value2:
    case value3:            // multiple cases, same action
        // code
        break;
    default:
        // code
}

// OWN BRACES INSIDE A CASE
case 1: {
    int x = 10;    // its own scope — doesn't clash with another x elsewhere
    break;
}

// WHILE WITH SEVERAL CONDITIONS
while (attempts > 0 && !granted) {
    ...
}
// repeats while ALL conditions joined with && are true
// just ONE being false is enough to exit

// DO-WHILE FOR MENUS
int option;
do {
    // show menu
    option = sc.nextInt();
    switch (option) { ... }
} while (option != exit);

// PARALLEL ARRAYS
String[] names  = new String[5];
boolean[] state = new boolean[5];
// same index i in both arrays = same element

// SEARCHING AN ARRAY
int index = -1;                // -1 = "not found yet"
for (int i = 0; i < count; i++) {
    if (array[i].equals(target)) {
        index = i;
        break;                  // found it — no need to keep searching
    }
}

// SCANNER — the usual trap
int n = sc.nextInt();
sc.nextLine();                  // ← required cleaner before nextLine()
String text = sc.nextLine();

// STRINGS — always compare with equals
s1.equals(s2)                   // ← correct
s1 == s2                        // ← dangerous, compares references

// TERNARY OPERATOR — for a single value based on a condition
String text = (condition) ? "yes" : "no";
Keep goingAll Java (en) articles

Sigue aprendiendo

Leave a Reply

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