Contents
- if / else if / else
- switch statement and switch expression
- for, while, and do-while
- Enhanced for-each loop
- break, continue, and labeled jumps
The if statement evaluates a boolean expression and executes its block only when the condition is true. Chain multiple conditions with else if; the first matching branch runs and the rest are skipped. The final else is a catch-all when no prior condition matched. For a single-expression conditional that produces a value, use the ternary operator condition ? valueIfTrue : valueIfFalse. Deeply nested if blocks are a readability smell — prefer guard clauses (early returns) to keep the happy path at the lowest indent level.
int score = 75;
// Simple if/else
if (score >= 90) {
System.out.println("A");
} else if (score >= 80) {
System.out.println("B");
} else if (score >= 70) {
System.out.println("C");
} else {
System.out.println("F");
}
// Ternary operator — compact single-expression conditional
String result = score >= 70 ? "Pass" : "Fail";
// Conditional with &&, ||, ! operators
boolean isAdult = true;
boolean hasTicket = false;
if (isAdult && hasTicket) {
System.out.println("Admitted");
} else if (!isAdult) {
System.out.println("Under age");
} else {
System.out.println("No ticket");
}
// Nested if — keep nesting shallow for readability
// Prefer early return (guard clause) over deeply nested if
String classify(int n) {
if (n < 0) return "negative";
if (n == 0) return "zero";
if (n % 2 == 0) return "positive even";
return "positive odd";
}
The traditional switch statement tests a single value against multiple case labels and falls through to the next case unless a break is present — a common source of bugs. Java 14 introduced the switch expression with arrow syntax (->): no fall-through, each arm is a single expression or block, and the whole expression yields a value you can assign. When an arm needs multiple statements, use a block and return the value with yield. Switch works on int, String, enums, and (Java 21+) patterns.
// Traditional switch statement (fall-through unless break is used)
String day = "MONDAY";
switch (day) {
case "MONDAY":
case "TUESDAY":
case "WEDNESDAY":
case "THURSDAY":
case "FRIDAY":
System.out.println("Weekday");
break;
case "SATURDAY":
case "SUNDAY":
System.out.println("Weekend");
break;
default:
System.out.println("Unknown");
}
// Switch expression (Java 14+) — arrow syntax, no fall-through, yields a value
String type = switch (day) {
case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" -> "Weekday";
case "SATURDAY", "SUNDAY" -> "Weekend";
default -> "Unknown";
};
// Switch expression with blocks and yield
int numLetters = switch (day) {
case "MONDAY", "FRIDAY", "SUNDAY" -> 6;
case "TUESDAY" -> 7;
case "THURSDAY", "SATURDAY" -> 8;
default -> {
System.out.println("Unexpected: " + day);
yield day.length(); // yield returns the value from a block
}
};
// Switch on integers and strings — both supported
int code = 2;
String label = switch (code) {
case 1 -> "ONE";
case 2 -> "TWO";
case 3 -> "THREE";
default -> "OTHER";
};
Java has three general-purpose loops. The classic for loop packs initialization, condition, and update into one line — ideal when you know the count or need an index. The while loop checks its condition before each iteration and is the right choice when the number of iterations is unknown. The do-while loop checks after the body executes, guaranteeing at least one run — useful for input validation or menu loops. All three can be combined with break to exit early or express an infinite loop with an internal escape condition.
// Classic for loop — index-based
for (int i = 0; i < 5; i++) {
System.out.print(i + " "); // 0 1 2 3 4
}
// Multiple variables in for loop
for (int i = 0, j = 10; i < j; i++, j--) {
System.out.println("i=" + i + " j=" + j);
}
// while loop — condition checked before body
int n = 10;
int sum = 0;
while (n > 0) {
sum += n;
n--;
}
System.out.println(sum); // 55
// do-while — body executes at least once
int input;
do {
input = readInput(); // hypothetical input method
} while (input < 0);
// Infinite loop with break
while (true) {
String line = readLine();
if (line == null || line.isEmpty()) break;
process(line);
}
// Iterating backwards
for (int i = list.size() - 1; i >= 0; i--) {
System.out.println(list.get(i));
}
The enhanced for loop (for-each) iterates over any array or Iterable<T> without managing an index variable. The compiler rewrites it to an iterator loop under the hood. It is the preferred way to iterate lists, sets, map entry sets, and custom collections when you do not need the position. The trade-off: you cannot safely remove elements during iteration (use removeIf or an explicit Iterator instead), and you cannot write back to the original array through the loop variable.
// for-each over an array
int[] numbers = {1, 2, 3, 4, 5};
for (int n : numbers) {
System.out.print(n + " ");
}
// for-each over a List
List<String> names = List.of("Alice", "Bob", "Charlie");
for (String name : names) {
System.out.println(name);
}
// for-each over a Map's entrySet
Map<String, Integer> scores = Map.of("Alice", 90, "Bob", 85);
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + " → " + entry.getValue());
}
// for-each works with any Iterable
Iterable<Path> paths = FileSystems.getDefault().getRootDirectories();
for (Path p : paths) {
System.out.println(p);
}
// Cannot modify collection during for-each (use iterator or removeIf instead)
// WRONG — throws ConcurrentModificationException:
// for (String s : names) { if (s.startsWith("A")) names.remove(s); }
// CORRECT:
names = new ArrayList<>(names);
names.removeIf(s -> s.startsWith("A")); // safe
The enhanced for-each loop is compiled to an iterator loop — it can be used with any class that implements Iterable<T>. It does not give you the index; use a classic for(int i=0...) loop when you need the position.
break immediately exits the innermost enclosing loop or switch. continue skips the remainder of the current iteration and jumps to the next one. Both can target an outer loop by prefixing it with a label — break outer exits the labeled loop entirely, while continue outer advances to the outer loop's next iteration. Labeled jumps are valid Java but should be used sparingly; extracting nested loops into a method with an early return is usually cleaner and easier to test.
// break — exits the innermost enclosing loop or switch
for (int i = 0; i < 10; i++) {
if (i == 5) break;
System.out.print(i + " "); // 0 1 2 3 4
}
// continue — skips the rest of the current iteration
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) continue; // skip even numbers
System.out.print(i + " "); // 1 3 5 7 9
}
// Labeled break — exits a specific outer loop by label
outer:
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) {
if (row == 2 && col == 3) {
System.out.println("Found at [" + row + "][" + col + "]");
break outer; // exits both loops
}
}
}
// Labeled continue — skip to next iteration of an outer loop
outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) continue outer; // skip rest of inner, advance outer
System.out.println("i=" + i + " j=" + j);
}
}
// Prints: i=0 j=0, i=1 j=0, i=2 j=0
// Prefer extracting nested loops into a method with early return
// over labeled break/continue — cleaner code
boolean containsPair(int[][] matrix, int target) {
for (int[] row : matrix)
for (int val : row)
if (val == target) return true; // early return instead of labeled break
return false;
}
Labeled break and continue are valid Java but can make code harder to follow. Prefer extracting nested loops into a helper method with an early return, which is usually clearer.