Java flow control exercises — the entry grade, the library and a three-attempt ATM (and the 9.5 that came out as “Pass”)
A classmate once showed me his grade validator, very proud of it: “it works, I’ve tested it”. He had tested it with a 6. I asked him to enter a 9.5 and the program answered, without batting an eyelid: “Pass”. No error, no exception, no compiler warning. Each condition was correctly written on its own; what was wrong was the order: the first one was grade >= 5, and 9.5 is also greater than or equal to 5. In an else if chain the first true condition wins and the rest are not even looked at. You’ll see that bug with its real output in “What’s wrong here?”, and the first exercise forces you to order them properly.
Here are three Java flow control exercises, from easier to harder: the university entry grade (ranges with else if and validating before calculating), the faculty library loan (switch on a String, each case with its own braces) and an ATM with three PIN attempts without loops and one operation with switch. As in the flow control practice, there are no loops or arrays: each run does just one thing. At the end you have the exam-style quiz and the cheat sheet.
Table of Contents
How to use this article
- Read the statement and the expected output of each exercise. The output is real: if your program prints the same with the same data, you’re on the right track.
- Try it without looking at anything for at least 15 minutes. If you get stuck, open the hint.
- Before the solutions, do “What’s wrong here?”: three fragments with one bug each. Predict what they print before reading the explanation.
- Compare your solution with the commented one. It doesn’t have to be identical: it has to give the same output with the same data and for the same reasons.
The outputs come from a single run with Java 21 on a computer set to Spanish, so decimals are typed and printed with a comma (11,8 instead of 11.8): Scanner and printf follow the computer’s language. On a computer set to English you would type 11.8 and see a point. What appears after each question is what was typed. In the site’s Java playground the typed data goes in the Input tab, one line per value.
Basic level
Exercise 1 — University entry grade validator
Write GradeValidator: it asks for a university entry grade (from 0 to 14, as in Spain’s university entrance exam with the optional subjects) and rates it like this:
| Grade | Rating |
|---|---|
| below 5 | Fail |
| from 5 to below 7 | Pass |
| from 7 to below 9 | Good |
| from 9 to below 11 | Very good |
| 11 or more | Excellent |
Then it says how far above or below the GCID cut-off grade it is, which we’ll take as 10.5 (made up), with two decimals. If the grade is outside 0-14, it warns and calculates nothing else. Finally it prints the unrounded margin with println, to see what the double really stores.
Expected output with 11,8:
=== ENTRY GRADE VALIDATOR === Entry grade (0-14): 11,8 --- Result --- Rating: Excellent You get into GCID: 1,30 above the cut-off (10,5). Unrounded margin: 1.3000000000000007
With 15:
=== ENTRY GRADE VALIDATOR === Entry grade (0-14): 15 Invalid data: the entry grade goes from 0 to 14.
With 4,99:
=== ENTRY GRADE VALIDATOR === Entry grade (0-14): 4,99 --- Result --- Rating: Fail You are 5,51 short of the cut-off (10,5). Unrounded margin: -5.51
Validate the range with an if at the start and stop with return; if it fails. For the rating, an else if chain that starts with the highest range (grade >= 11): that way each condition only needs the lower limit. The margin is grade - CUTOFF: if it’s negative, print -margin in the shortfall message. Why 1,30 with printf and something else with println? You’ll see in the solution.
Intermediate level
Exercise 2 — Library menu
Write Library: the faculty library handles one operation per run, chosen from a menu.
- 1. Borrow a book: asks for the member type, how many books they already have and the title. By type: student 15 days and 3 books at most; PDI (teaching and research staff) 30 days and 6 books; visitor 7 days and 1 book. If the type doesn’t exist, the title is missing or they already have the maximum, it says so; otherwise it confirms the loan.
- 2. Return a book: asks for the member type and the days late. On time, it thanks them; PDI staff pay no fine; everyone else pays 0.20 € per day up to 5 €.
- 3. See the rules.
- Any other number: “Invalid option”.
The member type is accepted in any mix of upper and lower case (PDI, pdi). The rules are made up.
Expected output for a loan and for a loan that doesn’t fit:
=== FACULTY LIBRARY === 1. Borrow a book 2. Return a book 3. See the rules Option: 1 Member type (student/pdi/visitor): student Books you already have: 1 Title: Clean Code Loan done: 'Clean Code' for 15 days (2 of 3 books).
=== FACULTY LIBRARY === 1. Borrow a book 2. Return a book 3. See the rules Option: 1 Member type (student/pdi/visitor): visitor Books you already have: 1 Title: Java in Depth You can't take any more: you already have 1 of 1.
Returns 9 days late, 40 days late (it hits the maximum) and from a PDI member typed in capitals:
=== FACULTY LIBRARY === 1. Borrow a book 2. Return a book 3. See the rules Option: 2 Member type (student/pdi/visitor): student Days late: 9 Fine: 1,80 € (0.20 € per day, 5 € at most).
=== FACULTY LIBRARY === 1. Borrow a book 2. Return a book 3. See the rules Option: 2 Member type (student/pdi/visitor): student Days late: 40 Fine: 5,00 € (0.20 € per day, 5 € at most).
=== FACULTY LIBRARY === 1. Borrow a book 2. Return a book 3. See the rules Option: 2 Member type (student/pdi/visitor): PDI Days late: 12 Returned 12 days late: PDI staff pay no fine.
And an option that doesn’t exist:
=== FACULTY LIBRARY === 1. Borrow a book 2. Return a book 3. See the rules Option: 5 Invalid option: 5
The menu is a switch (option). Inside case 1, another switch (kind) on a String that gives a value to days and maxBooks (with its break in each case, or with arrows). If you declare a variable with the same name (kind) in cases 1 and 2, put each case inside its own braces: case 1: { … break; }. Read the type with trim().toLowerCase() so you don’t depend on capitals. The fine: delay * 0.20 and, if it goes over 5, it stays at 5.
Final challenge
Exercise 3 — ATM with PIN
Write AtmPin: the right PIN is 4321 and there are three attempts without using loops. After each failure it says how many attempts are left; on the third failure, “Card blocked. Call your bank.” and the program ends. With the right PIN, a menu with one operation:
- 1. Check balance (it starts at 500.00 €).
- 2. Withdraw cash: the amount must be positive and a multiple of 10, at most 600 € per operation, and no more than the balance. Check in that order.
- 3. Deposit cash: from 1 to 1000 € per deposit.
Always read lines (nextLine() + Integer.parseInt), and store the PIN as text. Expected output getting it right on the second try and withdrawing 120 €:
=== ATM === PIN: 1111 Wrong PIN (2 attempts left) PIN: 4321 Correct PIN. 1. Check balance 2. Withdraw cash 3. Deposit cash Operation: 2 Amount (multiple of 10): 120 Take 120 €. Balance: 380,00 €
Three failures:
=== ATM === PIN: 1111 Wrong PIN (2 attempts left) PIN: 2222 Wrong PIN (1 attempt left) PIN: 0000 Card blocked. Call your bank.
Without enough balance, with an amount that can’t be given in notes and with a deposit:
=== ATM === PIN: 4321 Correct PIN. 1. Check balance 2. Withdraw cash 3. Deposit cash Operation: 2 Amount (multiple of 10): 550 Insufficient balance: you have 500,00 €.
=== ATM === PIN: 4321 Correct PIN. 1. Check balance 2. Withdraw cash 3. Deposit cash Operation: 2 Amount (multiple of 10): 125 Invalid amount: the machine only gives 10, 20 and 50 notes.
=== ATM === PIN: 4321 Correct PIN. 1. Check balance 2. Withdraw cash 3. Deposit cash Operation: 3 Amount to deposit: 200 Deposit done. Balance: 700,00 €
Without loops, the three attempts are three if (!pin.equals(PIN)), each one inside the previous one: the second PIN is only asked for if the first one failed. In the deepest one, block and return;. If you reach the line after the first if, some attempt was right. For the plural, attempts is 2 after the first failure and 1 after the second. Why "4321" and not 4321? Think of a PIN like 0420.
What’s wrong here?
Before the solutions, three fragments with one bug each: all three compile without warnings. Read each one and predict what it prints before looking at the output.
Mistake 1
public class ElseIfOrder {
public static void main(String[] args) {
double grade = 9.5;
String rating;
if (grade >= 5) {
rating = "Pass";
} else if (grade >= 7) {
rating = "Good";
} else if (grade >= 9) {
rating = "Very good";
} else {
rating = "Fail";
}
System.out.println(grade + " → " + rating);
}
}
What does it print with grade = 9.5? Real output:
9.5 → Pass
9.5 >= 5 is true, so it goes into the first branch and the chain ends there: the following else if are not evaluated. The diagram shows it on the grade line:
With grade >= 5 at the top, everything from 5 to 14 stays as “Pass”: the “Good” and “Very good” branches are dead code.
Ordering the ranges from lowest to highest using only the lower limit. Each condition is correct on its own, but the first one swallows everything. Notice the 9.5 in the output: it’s printed by + with a double, which always uses a point.
Fix: from highest to lowest.
public class ElseIfOrder {
public static void main(String[] args) {
double grade = 9.5;
String rating;
if (grade >= 9) { // from highest to lowest
rating = "Very good";
} else if (grade >= 7) {
rating = "Good";
} else if (grade >= 5) {
rating = "Pass";
} else {
rating = "Fail";
}
System.out.println(grade + " → " + rating);
}
}
9.5 → Very good
If you prefer going from lowest to highest, each condition needs both limits: grade >= 5 && grade < 7. It works, but it’s longer, and if you get a limit wrong, gaps appear (what happens to a 6.99 if you write grade <= 6.9?). From highest to lowest, no gap is possible.
Mistake 2
public class LoanDays {
public static void main(String[] args) {
String kind = "student";
int days = 0;
switch (kind) {
case "student":
days = 15;
case "pdi":
days = 30;
case "visitor":
days = 7;
}
System.out.println(kind + ": " + days + " days");
}
}
How many days does a student get? Real output:
student: 7 days
The break is missing in each case. Java enters case "student", sets days = 15, falls into case "pdi" and changes it to 30, falls into case "visitor" and leaves it at 7. The last value is the one that stays. It’s the same fall-through that added up instalments on purpose in the practice; here it wrecks the result.
Thinking the switch leaves on its own when a case ends. In the classic switch it only leaves with break (or by reaching the end). Without it, every case below runs.
Fix: the arrow switch, which also returns the value.
public class LoanDays {
public static void main(String[] args) {
String kind = "student";
int days = switch (kind) { // with arrows there is no fall-through
case "student" -> 15;
case "pdi" -> 30;
case "visitor" -> 7;
default -> 0; // in a switch that returns a value, default is mandatory
};
System.out.println(kind + ": " + days + " days");
}
}
student: 15 days
Mistake 3
public class ValidAge {
public static void main(String[] args) {
int age = 150;
if (age < 16 && age > 99) {
System.out.println("Age out of range");
} else {
System.out.println("Age correct: " + age);
}
}
}
Does it reject 150 years? Real output:
Age correct: 150
age < 16 && age > 99 asks for the age to be below 16 and at the same time above 99: no number meets both, so the condition is always false and everything falls into the else. With 150: 150 < 16 is false, and with && Java doesn’t even look at the second part (short circuit).
Translating “out of range” with &&. “Out” means “below or above”: ||. If you think of it in positive form (“inside” = age >= 16 && age <= 99), outside is its negation: !(age >= 16 && age <= 99), which by De Morgan’s laws is age < 16 || age > 99.
Fix:
public class ValidAge {
public static void main(String[] args) {
int age = 150;
if (age < 16 || age > 99) { // it is enough for ONE of the two to fail
System.out.println("Age out of range");
} else {
System.out.println("Age correct: " + age);
}
}
}
Age out of range
Commented solutions
Solution Exercise 1
import java.util.Scanner;
/**
* University entry grade (0-14): check the range BEFORE calculating, and rate it
* with else if from highest to lowest. Cut-off grade made up.
*/
public class GradeValidator {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
final double CUTOFF = 10.5; // GCID cut-off grade (made up)
System.out.println("=== ENTRY GRADE VALIDATOR ===");
System.out.print("Entry grade (0-14): ");
double grade = sc.nextDouble(); // computer set to Spanish: typed with a comma (11,8)
// 1. Validate first: if the data makes no sense, stop here and calculate nothing else
if (grade < 0 || grade > 14) {
System.out.println("Invalid data: the entry grade goes from 0 to 14.");
return;
}
// 2. Rate from HIGHEST to lowest: the first true condition wins and the rest are not even looked at.
// That is why "grade >= 9" is enough and "grade >= 9 && grade < 11" is not needed.
String rating;
if (grade >= 11) {
rating = "Excellent";
} else if (grade >= 9) {
rating = "Very good";
} else if (grade >= 7) {
rating = "Good";
} else if (grade >= 5) {
rating = "Pass";
} else {
rating = "Fail";
}
System.out.println();
System.out.println("--- Result ---");
System.out.println("Rating: " + rating);
// 3. Margin over the cut-off: positive if you get in, negative if you fall short
double margin = grade - CUTOFF;
if (margin >= 0) {
System.out.printf("You get into GCID: %.2f above the cut-off (%.1f).%n", margin, CUTOFF);
} else {
System.out.printf("You are %.2f short of the cut-off (%.1f).%n", -margin, CUTOFF);
}
// println ignores the computer's language: decimal point and every decimal the double has
System.out.println("Unrounded margin: " + margin);
sc.close();
}
}
Why it’s done this way:
- Validate first and stop with
return. If 15 carried on, it would come out as “Excellent” with a margin of 4.50: a result that looks right from impossible data. With thereturnat the start, everything after it can take for granted that the grade is between 0 and 14. - From highest to lowest. With 11,8 the first condition (
11.8 >= 11) is already true: “Excellent”. With 4,99 all four fail and it reaches theelse: “Fail”. No value between 0 and 14 is left without a rating. String rating;with no initial value. Java checks that every branch gives it a value before it’s used. If you remove the finalelse,javaccomplains:variable rating might not have been initialized. It’s a help: it warns you there are unrated grades.- The margin with
printfand withprintln. 11.8 − 10.5 should be 1.3, but in binary 11.8 isn’t exact and thedoublestores 1.3000000000000007.printf("%.2f")rounds to two decimals and uses the computer’s comma (1,30);printlnshows the number as it is, with a point, because it ignores the language. With 4,99 the result is −5.51 and here it does come out clean. That’s whyprintfis the way to show results. -marginin the shortfall message. “You are -5,51 short” reads oddly; with the sign flipped, “You are 5,51 short”.
| Typed grade | In range? | First true condition | Rating | Margin |
|---|---|---|---|---|
| 11,8 | Yes | grade >= 11 | Excellent | 11.8 − 10.5 = 1.30 |
| 15 | No: 15 > 14 | (not reached) | (not reached) | (not reached) |
| 4,99 | Yes | none: else | Fail | 4.99 − 10.5 = −5.51 |
Solution Exercise 2
import java.util.Scanner;
/**
* Faculty library: one operation per run (loan, return or rules), no arrays.
* Loan periods, limits and fines made up.
*/
public class Library {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("=== FACULTY LIBRARY ===");
System.out.println("1. Borrow a book");
System.out.println("2. Return a book");
System.out.println("3. See the rules");
System.out.print("Option: ");
int option = sc.nextInt();
sc.nextLine(); // Enter out before reading texts
switch (option) {
case 1: { // own braces: "kind" is also declared in case 2
System.out.print("Member type (student/pdi/visitor): ");
String kind = sc.nextLine().trim().toLowerCase();
System.out.print("Books you already have: ");
int has = sc.nextInt();
sc.nextLine();
System.out.print("Title: ");
String title = sc.nextLine().trim();
int days; // loan days
int maxBooks; // books at the same time
switch (kind) { // switch with String: compares with equals(), not with ==
case "student":
days = 15;
maxBooks = 3;
break;
case "pdi":
days = 30;
maxBooks = 6;
break;
case "visitor":
days = 7;
maxBooks = 1;
break;
default:
days = 0; // marker for "unknown kind"
maxBooks = 0;
}
if (days == 0) {
System.out.println("Unknown member type: " + kind);
} else if (title.isEmpty()) {
System.out.println("The title is missing.");
} else if (has >= maxBooks) {
System.out.println("You can't take any more: you already have " + has + " of " + maxBooks + ".");
} else {
System.out.printf("Loan done: '%s' for %d days (%d of %d books).%n",
title, days, has + 1, maxBooks);
}
break;
}
case 2: {
System.out.print("Member type (student/pdi/visitor): ");
String kind = sc.nextLine().trim().toLowerCase();
System.out.print("Days late: ");
int delay = sc.nextInt();
if (delay < 0) {
System.out.println("Invalid data.");
} else if (delay == 0) {
System.out.println("Returned on time. Thank you.");
} else if (kind.equals("pdi")) {
System.out.println("Returned " + delay + " days late: PDI staff pay no fine.");
} else {
double fine = delay * 0.20; // 20 cents per day...
if (fine > 5) {
fine = 5; // ...up to a maximum of 5 €
}
System.out.printf("Fine: %.2f € (0.20 € per day, 5 € at most).%n", fine);
}
break;
}
case 3:
System.out.println("Students: 3 books, 15 days.");
System.out.println("PDI: 6 books, 30 days.");
System.out.println("Visitors: 1 book, 7 days.");
System.out.println("Late: 0.20 € per day, 5 € at most (PDI staff don't pay).");
break;
default:
System.out.println("Invalid option: " + option);
}
sc.close();
}
}
Why it’s done this way:
- Own braces in
case 1: { … }andcase 2: { … }. The wholeswitchis a single block: if both cases declaredString kindwithout braces,javacwould sayvariable kind is already defined. With braces, eachkindonly lives inside its own case. Notice thebreakgoes inside the braces, at the end. - The inner
switchgives a value to two variables. That’s why it’s classic (withbreak) and not an expression: aswitchexpression returns one value. Thedefaultsetsdays = 0as a marker for “unknown type”, just like the certificate’s -1 in the practice. - The order of the loan chain. First the type (if it doesn’t exist,
maxBooksis 0 and “you already have 1 of 0” would make no sense), then the title, then the limit. With the visitor:1 >= 1is true, so they can’t take another one. has + 1in the message. They had 1 and take another: 2 of 3.- The fine with a cap. 9 × 0.20 = 1.80 €; 40 × 0.20 = 8.00 €, which is over 5 and stays at 5.00 €. A one-line alternative is
Math.min(delay * 0.20, 5). PDIin capitals works because it’s lowercased when read; withouttoLowerCase(),"PDI".equals("pdi")would befalseand the lecturer would pay a fine.
In case 2, sc.nextInt() is the last read, so the clean-up nextLine() isn’t needed: there’s nothing afterwards that could swallow the Enter. In case 1 it is, because the title comes next.
Solution Exercise 3
import java.util.Scanner;
/**
* ATM: PIN with 3 attempts WITHOUT a loop (one if inside another) and one operation with switch.
* Always read whole lines (nextLine + parseInt): that way there is no buffer trap.
*/
public class AtmPin {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
final String PIN = "4321"; // String, not int: a PIN like 0420 would lose its zero
final int LIMIT = 600; // euros per operation
double balance = 500.0;
int attempts = 3;
System.out.println("=== ATM ===");
System.out.print("PIN: ");
String pin = sc.nextLine().trim();
if (!pin.equals(PIN)) { // failure 1
attempts--;
System.out.println("Wrong PIN (" + attempts + " attempts left)");
System.out.print("PIN: ");
pin = sc.nextLine().trim();
if (!pin.equals(PIN)) { // failure 2
attempts--;
System.out.println("Wrong PIN (" + attempts + " attempt left)");
System.out.print("PIN: ");
pin = sc.nextLine().trim();
if (!pin.equals(PIN)) { // failure 3: that's it
System.out.println("Card blocked. Call your bank.");
return; // without this, the program would carry on to the menu
}
}
}
// If we get here, some attempt was right
System.out.println("Correct PIN.");
System.out.println();
System.out.println("1. Check balance");
System.out.println("2. Withdraw cash");
System.out.println("3. Deposit cash");
System.out.print("Operation: ");
int op = Integer.parseInt(sc.nextLine().trim());
switch (op) {
case 1 -> System.out.printf("Balance: %.2f €%n", balance);
case 2 -> {
System.out.print("Amount (multiple of 10): ");
int amount = Integer.parseInt(sc.nextLine().trim());
// From the most general to the most specific: format, machine limit and, last, the balance
if (amount <= 0 || amount % 10 != 0) {
System.out.println("Invalid amount: the machine only gives 10, 20 and 50 notes.");
} else if (amount > LIMIT) {
System.out.println("Over the limit per operation (" + LIMIT + " €).");
} else if (amount > balance) {
System.out.printf("Insufficient balance: you have %.2f €.%n", balance);
} else {
balance -= amount;
System.out.printf("Take %d €. Balance: %.2f €%n", amount, balance);
}
}
case 3 -> {
System.out.print("Amount to deposit: ");
int amount = Integer.parseInt(sc.nextLine().trim());
if (amount > 0 && amount <= 1000) {
balance += amount;
System.out.printf("Deposit done. Balance: %.2f €%n", balance);
} else {
System.out.println("Invalid amount (1 to 1000 € per deposit).");
}
}
default -> System.out.println("Invalid operation.");
}
sc.close();
}
}
Why it’s done this way:
- Three nested
ifstatements. The second PIN is only asked for if the first one failed, and the third if both failed: each attempt goes inside the previous one. The diagram shows the paths:
- The
returnon blocking is essential. Without it, after the third failure the program would leave the threeifstatements and reach “Correct PIN.”. In the practice, the guessing game used the other style (one block after another andreturnon a hit); both are valid, and both are crying out for a loop. - The PIN as a
String. Withint,0420would be read as 420 and a three-digit PIN would pass as valid. Also, comparing withequalsavoids aNumberFormatExceptionif someone types letters. - Always reading lines.
Integer.parseInt(sc.nextLine().trim())leaves no Enter in the buffer, so the clean-upnextLine()isn’t needed anywhere. - The order of the withdrawal checks. First the format (positive and a multiple of 10), then the machine’s limit and finally the balance. With 550: it’s a multiple of 10 and not over 600, so it reaches the third one: 550 > 500, insufficient balance. With 125:
125 % 10is 5, not 0, and it stops at the first one. - The sums. 500 − 120 = 380.00 €; 500 + 200 = 700.00 €.
| Typed | Condition | Result |
|---|---|---|
1111 | !"1111".equals("4321") → true | attempts 3 → 2, another one is asked |
4321 | !"4321".equals("4321") → false | leaves the if statements: correct PIN |
2, 120 | 120 % 10 == 0, 120 <= 600, 120 <= 500 | balance 380.00 € |
Exam-style quiz
Now test yourself as in the exam. The FP2 Java quiz works like this: 15 questions in 15 minutes covering the whole Java syllabus, with a few from each topic. Here are the flow control ones: answer without running anything, check and read the explanation of the ones you miss. These questions also make up the bank of the FP2 theory mock exam.
What does this fragment print?
int mark = 7;
if (mark >= 5) System.out.print("A");
else if (mark >= 7) System.out.print("B");
else System.out.print("C");
System.out.println();ABABC
Solution
Answer: A
The conditions are tested from top to bottom and the first one that holds runs. Since 7 >= 5, A is printed and else if (mark >= 7) is never evaluated. That’s why a chain of ranges starts with the most restrictive one.
What does this fragment print?
int day = 2;
switch (day) {
case 1: System.out.print("L");
case 2: System.out.print("M");
case 3: System.out.print("X"); break;
default: System.out.print("?");
}
System.out.println();Solution
Answer: MX
The classic switch jumps to case 2 and prints M, but as there’s no break it keeps falling into case 3 (prints X) until it finds the break. This is the famous fall-through: forgetting a break is one of the most common mistakes.
True or false?
- Java has no
elif: you writeelse if. - With
int x, the conditionif (x = 5)doesn’t compile. - Since Java 7 you can
switchon aString. a && balways evaluates both sides.- In a
switchwith arrows (case 1 -> …) there’s no fall-through.
Solution
- T — Java has no
elif: you writeelse if. - T — With
int x, the conditionif (x = 5)doesn’t compile. - T — Since Java 7 you can
switchon aString. - F —
a && balways evaluates both sides. - T — In a
switchwith arrows (case 1 -> …) there’s no fall-through.
if (x = 5) assigns instead of comparing and the result is an int, not a boolean: Java won’t accept it (in C it would compile). && short-circuits: if the first part is false, the second isn’t evaluated. And the switch with -> only runs its own branch, with no need for break.
What does this fragment print?
int a = 4, b = 9; String r = (a > b) ? "a" : (b % 2 == 0 ? "b even" : "b odd"); System.out.println(r);
Solution
Answer: b odd
The ternary operator is condition ? ifTrue : ifFalse. 4 > 9 is false, so the second ternary is evaluated: 9 % 2 is 1, not 0, so the result is "b odd". It’s the equivalent of Python’s x if c else y.
What does this fragment print?
String s = new String("hello");
if (s == "hello") System.out.println("equal");
else System.out.println("different");differentequal- It doesn’t compile
- It throws an exception
Solution
Answer: different
With objects, == checks whether both variables point to the same place in memory. To compare the text you use equals (or equalsIgnoreCase). With String the bug is sneaky because == sometimes gets it right by chance (equal literals share an object).
What does this fragment print?
int x = 0;
if (x != 0 && 10 / x > 1) System.out.println("big");
else System.out.println("safe");Solution
Answer: safe
x != 0 is false, so && already knows the whole thing is false and doesn’t evaluate 10 / x. Thanks to that there’s no ArithmeticException from dividing by zero. With & (no short-circuit) it would be evaluated and the program would crash.
Fill in the rules of the Java if:
The condition always goes in ____ and must be of type ____. The block goes in ____; if you leave them out, only the ____ statement depends on the if.
Words:
- brackets
- boolean
- first
- square brackets
- indentation
- last
- braces
- int
Solution
The condition always goes in brackets and must be of type boolean. The block goes in braces; if you leave them out, only the first statement depends on the if.
Without braces, indentation means nothing to Java: only the first statement is inside the if and the following ones always run. That’s why braces are recommended even for a single line.
What does this fragment print (Java 14 or later)?
String day = "sat";
String kind = switch (day) {
case "sat", "sun" -> "weekend";
default -> "weekday";
};
System.out.println(kind);Solution
Answer: weekend
The modern switch can return a value and group several cases with commas. "sat" matches the first case, so kind is "weekend". Notice the final ;: the whole switch is an expression inside an assignment.
Match each operator with what it does:
&& | |
|| | |
! | |
== | |
% |
Definitions:
- Remainder of integer division
- Logical OR with short-circuit
- Power
- Concatenation of conditions
- Logical AND with short-circuit
- Equality (on objects it compares references)
- Negation
Solution
&&→ Logical AND with short-circuit||→ Logical OR with short-circuit!→ Negation==→ Equality (on objects it compares references)%→ Remainder of integer division
In Java you don’t write and, or or not as in Python: they’re &&, || and !. Power isn’t an operator (Math.pow(a, b)) and % gives the remainder (9 % 2 is 1).
What does this fragment print?
int t = 20;
if (t > 25)
System.out.print("hot ");
System.out.print("beach");
System.out.println();beach- An empty line
hot beach- It doesn’t compile
Solution
Answer: beach
The indentation makes you think both lines are inside the if, but Java only looks at braces. As there are none, only print("hot ") depends on the if. It’s the bug of an if without braces that later gets an extra line.
With int a = 5, b = 0;, which conditions are true?
a > 3 && b == 0a < 3 || b == 0a % 2 == 1!(a == 5)b != 0 && a / b > 1
Solution
Answer: a > 3 && b == 0 · a < 3 || b == 0 · a % 2 == 1
&& needs both parts true; ||, at least one. 5 % 2 is 1, so the third is true. The last one is false without throwing any exception thanks to the short-circuit.
Watch it step by step in Python Tutor
Python Tutor (pythontutor.com) runs code line by line and marks which line runs at each step. Choose Java in the drop-down and paste this program, which rates the same 9.5 with the wrong order and with the right one:
public class TutorGrade {
public static void main(String[] args) {
double grade = 9.5;
String wrong;
if (grade >= 5) {
wrong = "Pass";
} else if (grade >= 9) {
wrong = "Very good";
} else {
wrong = "Fail";
}
String right;
if (grade >= 9) {
right = "Very good";
} else if (grade >= 5) {
right = "Pass";
} else {
right = "Fail";
}
System.out.println(wrong + " / " + right);
}
}
Output:
Pass / Very good
What to watch step by step:
- In the first chain, the arrow goes through
if (grade >= 5), goes in and jumps straight to theString right;line: theelse iflines are never visited. - In the variables panel,
wrong = "Pass"appears whilegradeis still 9.5. - In the second chain, the first condition (
grade >= 9) is already true: it goes in at the first one. - Count the steps of each chain: both stop as soon as they find a true condition. What changes is which one they find first.
Cheat sheet: Java flow control
The essentials in 30 seconds: validate the data and stop with return before calculating; in an else if chain the first true condition wins, so the most restrictive goes first; texts are compared with equals(); “out of range” is || and “in range” is &&; in a classic switch each case needs its break (or use arrows); reading lines with nextLine() + parseInt avoids the buffer trap.
| I want to… | Code | Result |
|---|---|---|
| Check that it’s within a range | if (x >= 0 && x <= 14) | both at once |
| Detect that it’s outside | if (x < 0 || x > 14) | one is enough |
| Rate by ranges | if (n >= 11) … else if (n >= 9) … else … | from highest to lowest |
| Compare texts | a.equals(b) / a.equalsIgnoreCase(b) | true if they say the same |
Compare with a fixed text with no null risk | "y".equalsIgnoreCase(r) | never throws |
| End the program early | return; inside main | it ends there |
| Choose by a value | switch (op) { case 1 -> …; default -> …; } | no fall-through |
| Calculate by a value | int d = switch (k) { case "a" -> 15; default -> 0; }; | default mandatory |
| Pile up on purpose | case 0: x += p; case 1: x += p; break; | falls from one case to the next |
A variable of its own in a case | case 1: { String k = …; break; } | own braces |
| Read a number without the trap | Integer.parseInt(sc.nextLine().trim()) | leaves no Enter |
| A repeatable random number | new Random(42).nextInt(100) + 1 | 1 to 100, always the same |
Templates from the exercises:
| Exercise | Pattern | With this data | Gives |
|---|---|---|---|
| Entry grade | validate + return, else if from highest to lowest, printf("%.2f") | 11,8 | Excellent, 1,30 above |
| Library | menu switch with braces per case + switch on a String | student, 1 book | 15 days (2 of 3 books) |
| Library (fine) | if with a cap | 40 days late | 5,00 € |
| ATM | three nested if (!pin.equals(PIN)) + return | 1111, 4321, withdraw 120 | balance 380,00 € |
| Symptom | Likely cause | ||
|---|---|---|---|
| High grades rated as low | else if ordered from lowest to highest with only the lower limit | ||
| Two equal texts “don’t match” | compared with == instead of equals() | ||
A switch gives the value of the last case | missing break (fall-through) | ||
| Impossible data is accepted | && where ` | ` should be (the condition is never true) | |
variable … is already defined in a switch | two case labels declare the same variable without their own braces | ||
variable … might not have been initialized | some branch of the if doesn’t give the variable a value | ||
| The program carries on after blocking | missing return; | ||
InputMismatchException with a decimal | a point was typed on a computer set to Spanish (or the other way round) |
Before handing it in, ask yourself:
- Have I tried a value from each branch, including the limits (5, 7, 9, 11) and one out of range?
- Are all texts compared with
equals()? - Does each
caseof a classicswitchend inbreak, except those that fall through on purpose? - Do I validate the data before using it (before
substring, dividing or rating)?
That wraps up flow control. In the next topic, Java loops, the guessing game and the ATM stop copying their block three times: a while repeats whatever is needed.
Did this article help you? Here is how you can support SergioLearns →




IC2 practical mock exam — flowcharts, fork and threads in C (and the screenshots you can’t leave out)