Java · lesson 6 of 66
Beginner
17- 1 Introduction to Java
- 2 Setting Up Java
- 3 Data Types in Java
- 4 Operators in Java
- 5 Variables and Operators in Java
- 6 Control Flow in Java
- 7 Arrays in Java
- 8 Functions (Methods) in Java
- 9 Strings in Java
- 10 Methods in Java
- 11 Java OOP — Complete Guide
- 12 Introduction to Object-Oriented Programming in Java
- 13 Encapsulation in Java
- 14 Inheritance in Java
- 15 Polymorphism in Java
- 17 Abstraction in Java
- 18 Error Types in Java
Intermediate
38- 16 Regular Expressions in Java
- 19 Interfaces in Java — A Deep Dive
- 20 Abstract Classes in Java
- 21 Method Overloading in Java
- 22 Composition vs Inheritance in Java
- 23 Generics in Java
- 27 Exception Handling in Java
- 28 Java Streams API
- 29 Collections Framework in Java
- 30 Optional in Java
- 31 File Handling in Java
- 32 Records in Java
- 33 Java 8 Features
- 34 Sealed Classes and Interfaces in Java
- 35 Multithreading in Java
- 37 JDBC — Database Access in Java
- 39 Maven — Java Build and Dependency Management
- 42 Gradle — Modern Java Build Tool
- 44 Unit Testing with JUnit 5 and Mockito
- 45 Logging in Java with SLF4J and Logback
- 49 Spring Boot — Introduction
- 50 Dependency Injection in Spring Boot
- 51 Java Project Ideas
- 52 Building REST APIs with Spring Boot
- 53 Validation in Spring Boot
- 54 Spring Data JPA — Database Access
- 55 Exception Handling in Spring Boot
- 56 Spring Security — Authentication and Authorization
- 57 JWT Authentication in Spring Boot
- 58 Dockerizing Spring Boot Applications
- 59 Java Enums: Beyond Simple Constants
- 60 Inner and Anonymous Classes in Java
- 61 Java Generics: Bounded Types, Wildcards, and Generic Methods
- 62 Java Date/Time API: LocalDate, LocalDateTime, and Duration
- 63 Java NIO.2: Paths, Files, and WatchService
- 64 Java Annotations: Custom Types and Meta-Information
- 65 Java Reflection: Dynamic Type Inspection and Method Invocation
- 66 Java Localization: ResourceBundle and Locale Support
Advanced
11Control Flow in Java
Master Java's decision-making and looping constructs — if/else, switch expressions, for/while/do-while loops, break, and continue.
By SkillByExample · Updated
Control flow decides which statements run and how often. By default Java runs top to bottom;
conditionals branch and loops repeat. Every example is a single file run with the JDK 21 source
launcher (java File.java), so you can paste it and run it unchanged.
if / else if / else
Java tests each condition in order and runs the first branch that is true; the rest are skipped.
public class Grades {
public static void main(String[] args) {
int[] scores = {95, 82, 82, 70, 41};
for (int score : scores) {
String grade;
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else if (score >= 70) {
grade = "C";
} else {
grade = "F";
}
System.out.println(score + " -> " + grade);
}
}
}
$ java Grades.java
95 -> A
82 -> B
82 -> B
70 -> C
41 -> F
95 also satisfies score >= 80 and score >= 70, but only the first true branch runs, so the
order of the tests matters: put the narrowest range first.
Missing braces
Braces are optional around a single statement, which makes this a classic bug:
public class Braces {
public static void main(String[] args) {
int balance = 50;
if (balance < 0)
System.out.println("Overdrawn!");
System.out.println("Fee charged");
System.out.println("Balance: " + balance);
}
}
$ java Braces.java
Fee charged
Balance: 50
The indentation suggests both lines belong to the if, but only the first does. “Fee charged”
prints even though the account is not overdrawn. Always use braces.
Classic switch and fall-through
The colon form of switch jumps to the matching case and keeps running until a break.
public class FallThrough {
public static void main(String[] args) {
int day = 3;
switch (day) {
case 1:
System.out.println("Monday");
case 3:
System.out.println("Wednesday");
case 4:
System.out.println("Thursday");
break;
default:
System.out.println("Other");
}
}
}
$ java FallThrough.java
Wednesday
Thursday
case 3 has no break, so execution falls into case 4 and prints “Thursday” too. Each case
needs its own break — or use the arrow form below, which never falls through.
switch expressions (Java 14+)
The arrow form yields a value, groups labels with commas, and runs only the matching branch.
public class DayNames {
public static void main(String[] args) {
for (int day : new int[] {3, 6, 9}) {
String name = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
case 4 -> "Thursday";
case 5 -> "Friday";
case 6, 7 -> "Weekend";
default -> "Invalid day " + day;
};
System.out.println(name);
}
}
}
$ java DayNames.java
Wednesday
Weekend
Invalid day 9
Because a switch expression must produce a value, the compiler rejects it if some input has no
branch. For an int that means a default is required; for an enum listing every constant is enough.
yield in a block
When a branch needs several statements, use a block and yield the value.
public class Yield {
public static void main(String[] args) {
for (int score : new int[] {82, 64, 30}) {
String result = switch (score / 10) {
case 10, 9 -> "A";
case 8 -> "B";
case 7 -> "C";
default -> {
System.out.println(" (below 70, checking pass mark for " + score + ")");
yield score >= 60 ? "D" : "F";
}
};
System.out.println(score + " -> " + result);
}
}
}
$ java Yield.java
82 -> B
(below 70, checking pass mark for 64)
64 -> D
(below 70, checking pass mark for 30)
30 -> F
score / 10 is integer division, so 82 becomes 8 and matches case 8 directly. The default
block only runs for 64 and 30. switch also works on String, char and enum values, and
(since Java 21) on patterns.
for loops
Use a for loop when you know how many iterations you need: initialise, test, update.
public class ForLoops {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) System.out.print(i + " ");
System.out.println();
for (int i = 10; i > 0; i -= 3) System.out.print(i + " ");
System.out.println();
for (int i = 0, j = 4; i < j; i++, j--) System.out.println("i=" + i + " j=" + j);
String[] fruits = {"apple", "banana", "cherry"};
for (String fruit : fruits) System.out.print(fruit.toUpperCase() + " ");
System.out.println();
}
}
$ java ForLoops.java
0 1 2 3 4
10 7 4 1
i=0 j=4
i=1 j=3
APPLE BANANA CHERRY
The countdown stops at 1 because the next value, -2, fails i > 0. The two-variable loop stops
when i and j meet at 2. The enhanced for (for-each) walks an array or collection without an
index — use it unless you need the position.
while and do-while
while checks first, so its body may run zero times. do-while runs the body once before checking.
public class Whiles {
public static void main(String[] args) {
int n = 100;
while (n < 10) {
System.out.println("while body ran");
}
do {
System.out.println("do-while body ran with n=" + n);
} while (n < 10);
int num = 1234, digitSum = 0;
while (num > 0) {
digitSum += num % 10;
num /= 10;
}
System.out.println("digit sum: " + digitSum);
}
}
$ java Whiles.java
do-while body ran with n=100
digit sum: 10
The while body never ran because 100 < 10 was false from the start; the do-while body ran
once anyway. The digit-sum loop doesn’t know in advance how many digits there are, which is when
while fits better than for.
Reading input until a sentinel
A while loop is the natural way to read until a stop value. Here the input is piped in.
import java.util.Scanner;
public class SumInput {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int sum = 0;
int input = sc.nextInt(); // priming read
while (input != 0) {
sum += input;
input = sc.nextInt();
}
System.out.println("Sum: " + sum);
}
}
$ printf "10\n20\n5\n0\n" | java SumInput.java
Sum: 35
The read before the loop gives the condition something to test on the first pass; the read at the end of the body fetches the next value. The 0 ends the loop and is never added.
break, continue and labels
break leaves a loop, continue skips to the next iteration, and a labelled break leaves an
outer loop.
public class BreakContinue {
public static void main(String[] args) {
int[] nums = {7, 3, 9, 4, 11, 2};
for (int n : nums) {
if (n % 2 == 0) {
System.out.println("first even: " + n);
break;
}
}
for (int i = 1; i <= 10; i++) {
if (i % 3 != 0) continue;
System.out.print(i + " ");
}
System.out.println();
outer:
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (i * j == 6) {
System.out.println("found i=" + i + " j=" + j);
break outer;
}
}
}
}
}
$ java BreakContinue.java
first even: 4
3 6 9
found i=2 j=3
break stopped at 4, so 2 was never examined. Without the outer label, break would leave
only the inner loop and the search would continue, also finding i=3 j=2.
Project: number guessing game
A seeded Random makes the secret repeatable, and the guesses are piped in so the run is
reproducible.
import java.util.Random;
import java.util.Scanner;
public class GuessingGame {
public static void main(String[] args) {
int secret = new Random(42).nextInt(100) + 1;
Scanner sc = new Scanner(System.in);
int attempts = 0, guess;
do {
guess = sc.nextInt();
attempts++;
if (guess < secret) System.out.println(guess + " is too low");
else if (guess > secret) System.out.println(guess + " is too high");
else System.out.println(guess + " is correct in " + attempts + " attempts");
} while (guess != secret);
}
}
$ printf "50\n25\n37\n" | java GuessingGame.java
50 is too high
25 is too low
37 is too high
Exception in thread "main" java.util.NoSuchElementException
at java.base/java.util.Scanner.throwFor(Scanner.java:945)
at java.base/java.util.Scanner.next(Scanner.java:1602)
at java.base/java.util.Scanner.nextInt(Scanner.java:2267)
at java.base/java.util.Scanner.nextInt(Scanner.java:2221)
at GuessingGame.main(GuessingGame.java:10)
The input ran out before the secret was found, and nextInt() throws NoSuchElementException
at end of input. The secret is between 26 and 36, so halving the gap again finds it:
$ printf "50\n25\n37\n31\n" | java GuessingGame.java
50 is too high
25 is too low
37 is too high
31 is correct in 4 attempts
Halving the remaining range each time is binary search; it finds any number from 1 to 100 in at
most 7 guesses. The do-while fits because a guess must be read before there is anything to compare.
Practice
- Print FizzBuzz for 1 to 15: “Fizz” for multiples of 3, “Buzz” for multiples of 5, “FizzBuzz” for both, the number otherwise.
Solution
public class FizzBuzz {
public static void main(String[] args) {
for (int i = 1; i <= 15; i++) {
if (i % 15 == 0) System.out.println("FizzBuzz");
else if (i % 3 == 0) System.out.println("Fizz");
else if (i % 5 == 0) System.out.println("Buzz");
else System.out.println(i);
}
}
}
$ java FizzBuzz.java
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
i % 15 must be tested first; otherwise 15 would match % 3 and print “Fizz”.
- Print a right triangle and a pyramid of
*with 4 rows using nested loops.
Solution
public class Patterns {
public static void main(String[] args) {
int n = 4;
for (int i = 1; i <= n; i++) {
System.out.println("*".repeat(i));
}
for (int i = 1; i <= n; i++) {
for (int s = i; s < n; s++) System.out.print(" ");
for (int k = 1; k <= 2 * i - 1; k++) System.out.print("*");
System.out.println();
}
}
}
$ java Patterns.java
*
**
***
****
*
***
*****
*******
Row i of the pyramid has n - i spaces and 2i - 1 stars, so each row grows by two stars and
shrinks by one space.
- Change
break outer;inBreakContinueto plainbreak;and print every match. How many lines appear?
Solution
public class InnerBreak {
public static void main(String[] args) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (i * j == 6) {
System.out.println("found i=" + i + " j=" + j);
break;
}
}
}
}
}
$ java InnerBreak.java
found i=2 j=3
found i=3 j=2
The unlabelled break leaves only the j loop, so the i loop continues and finds the second match.
- Instead of halving, guess in steps of 10 and then step down: feed
10 20 30 40 35 32 31to the guessing game. How many attempts does it take?
Solution
import java.util.Random;
import java.util.Scanner;
public class GuessingGame {
public static void main(String[] args) {
int secret = new Random(42).nextInt(100) + 1;
Scanner sc = new Scanner(System.in);
int attempts = 0, guess;
do {
guess = sc.nextInt();
attempts++;
if (guess < secret) System.out.println(guess + " is too low");
else if (guess > secret) System.out.println(guess + " is too high");
else System.out.println(guess + " is correct in " + attempts + " attempts");
} while (guess != secret);
}
}
$ printf "10\n20\n30\n40\n35\n32\n31\n" | java GuessingGame.java
10 is too low
20 is too low
30 is too low
40 is too high
35 is too high
32 is too high
31 is correct in 7 attempts
Seven attempts against four for halving. The loop ends as soon as guess != secret is false,
so it reads exactly as many numbers as it needs.