Control Flow
|
This section documents the current Java release line, with Java 25 LTS as the reference point (no specific patch version is pinned), as published at the Java developer portal, the Java Tutorials, and the Java SE API specification — which are the references these pages are written and verified against. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Control-flow statements decide which code runs and how often. This page follows the
dev.java "Controlling the Flow of Your Program"
and dev.java "Branching with Switch Expressions"
tracks; the normative rules are in
JLS chapter 14 ("Blocks, Statements, and
Patterns") and, for switch expressions,
JLS 15.28.
if/else, while, and do/while
if runs a block when a boolean condition is true; the optional else runs otherwise, and else if
chains further tests. The condition must be a boolean — Java has no "truthy" values. See
The if-then and if-then-else
Statements.
int score = 72;
if (score >= 90) {
System.out.println("A");
} else if (score >= 60) {
System.out.println("pass");
} else {
System.out.println("fail");
}
while tests before each iteration, so its body may run zero times; do/while tests after, so the
body always runs at least once. See
The while and do-while Statements.
int n = 5;
while (n > 0) {
System.out.print(n + " "); // 5 4 3 2 1
n--;
}
int attempts = 0;
do {
attempts++;
} while (attempts < 3); // body ran 3 times
A ternary conditional expression cond ? a : b is a value, not a statement — prefer it over a
multi-line if/else that only assigns one variable. return inside a method, and break/continue
inside a loop, also transfer control (see below).
for, Enhanced for, break, continue, and Labels
The classic for gathers initialization, test, and update on one line; any of the three parts may be
omitted, and for (;;) loops forever. See
The for Statement.
for (int i = 0; i < 5; i++) {
System.out.print(i); // 01234
}
The enhanced for (for-each) iterates the elements of an array or of anything implementing
Iterable,
without an index variable. Use it whenever you do not need the index.
int[] primes = {2, 3, 5, 7, 11};
int sum = 0;
for (int p : primes) {
sum += p; // 28
}
var names = java.util.List.of("Ada", "Bo", "Cy");
for (var name : names) { // works for any Iterable
System.out.println(name);
}
break exits the innermost loop or switch; continue skips to the next iteration. A labeled
statement lets either one target an outer loop — the only place a label is meaningful in Java, and its
structured stand-in for goto (a reserved word with no function). See
Branching Statements.
outer:
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
if (row + col == 3) {
break outer; // leaves BOTH loops
}
if (col == row) {
continue outer; // next row
}
System.out.println(row + "," + col);
}
}
switch Statements
A switch statement transfers control to the case label matching the selector and keeps executing — falling through subsequent cases — until a break, yield, return, or the end of the block.
default handles any unmatched value. With traditional colon labels the selector may be an integral
type, an
enum constant, or a
String. See
The switch Statement.
enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
Day day = Day.SAT;
String kind;
switch (day) {
case SAT:
case SUN:
kind = "weekend";
break; // WITHOUT this, execution falls into default
default:
kind = "weekday";
}
System.out.println(kind); // weekend
String command = "add";
switch (command) { // switch on a String
case "add" -> System.out.println("adding"); // arrow form: no fall-through
case "remove" -> System.out.println("removing");
default -> System.out.println("unknown");
}
A missing break is a classic bug source. The arrow label (→) runs exactly one branch and never
falls through; prefer it for new code. Colon and arrow labels cannot be mixed in one switch.
switch Expressions
Since Java 14 a switch can be an expression that produces a value (JEP 361). Arrow labels give the
result directly; a braced block supplies its value with yield. Several constants can share one label,
comma-separated.
int numLetters = switch (day) {
case MON, FRI, SUN -> 6;
case TUE -> 7;
case THU, SAT -> 8;
case WED -> 9;
};
String plan = switch (day) {
case SAT, SUN -> "rest";
default -> {
int idx = day.ordinal();
yield "workday #" + (idx + 1); // yield supplies this block's value
}
};
A switch expression must be exhaustive — every possible selector value is covered. Switching over an
enum with a case for each constant needs no default; the compiler adds a hidden branch that throws
MatchException
if the enum gains a constant later. For any other selector type you must write default.
jshell> String size = switch (3) { case 1 -> "one"; case 2, 3 -> "few"; default -> "many"; };
size ==> "few"
The selector can also be matched with type patterns, record deconstruction patterns, and when
guards — a full treatment is on Pattern Matching.
Object value = 42;
String text = switch (value) {
case Integer i when i > 0 -> "positive int " + i;
case Integer i -> "int " + i;
case String s -> "string of length " + s.length();
case null -> "null";
default -> "something else";
};
Statement switch vs. Expression switch
The flowchart contrasts a fall-through switch statement — where each case needs its own break — with the equivalent arrow-form switch expression, which runs one branch and produces one value.
See Also
-
Pattern Matching — type patterns, record patterns, and
whenguards inswitch. -
Operators and Expressions — the ternary conditional operator and boolean short-circuiting.
-
Arrays — the enhanced
forover arrays. -
Enums —
switchover enum constants and exhaustiveness.