ZetCode

Java flow control

Last modified October 13, 2026 Java 25

In this article we cover program flow control in Java. We show conditional statements, the switch statement and expression including pattern matching, loops, and the break and continue statements.

Control flow statements

By default, the statements of a method are executed one by one, from top to bottom. Control flow statements alter this order: they execute code only under a specific condition, repeat it multiple times, or jump out of it.

The examples use compact source files with an instance main method, which are final since Java 25. Programs that read user input use the IO.readln method, also new in Java 25.

The if statement

The if statement has the following general form:

if (expression) {

    statement;
}

The if keyword checks whether a boolean expression is true. If it is, the statement that follows is executed. The statement can be a single statement or a block. A block is a sequence of statements enclosed in curly brackets. The brackets are optional if the body has only one statement, but using them always is a widely followed convention that prevents mistakes when statements are added later.

Main.java
import java.util.Random;

void main() {

    var r = new Random();
    int num = r.nextInt(-10, 11);

    if (num > 0) {

        System.out.println("The number is positive");
    }
}

A random number is generated. If the number is greater than zero, we print a message to the terminal.

var r = new Random();
int num = r.nextInt(-10, 11);

These two lines generate a random integer between -10 and 10. The second argument of nextInt is exclusive.

if (num > 0) {

    System.out.println("The number is positive");
}

The if keyword is followed by an expression in round brackets. The expression must evaluate to a boolean value; unlike in C, an integer is not accepted. If the value is true, the block is executed. If the number is zero or negative, nothing is printed.

$ java Main.java
The number is positive
$ java Main.java
$ java Main.java
The number is positive

The else keyword

We can use the else keyword to create a simple branch. If the expression in the round brackets following the if keyword evaluates to false, the statement following the else keyword is executed.

Main.java
import java.util.Random;

void main() {

    var r = new Random();
    int num = r.nextInt(-10, 11);

    if (num > 0) {

        System.out.println("The number is positive");
    } else {

        System.out.println("The number is zero or negative");
    }
}

Exactly one of the two blocks is executed: either the block following the if keyword or the block following the else keyword.

$ java Main.java
The number is zero or negative
$ java Main.java
The number is positive
$ java Main.java
The number is zero or negative

Multiple branches with else if

We can create multiple branches with else if. Each else if tests another condition if and only if all previous conditions were false. We can chain as many else if branches as needed.

The previous program could not tell zero from negative numbers. The following program handles the three cases separately.

Main.java
void main() {

    String input = IO.readln("Enter an integer: ");
    int num = Integer.parseInt(input.trim());

    if (num < 0) {

        System.out.println("The integer is negative");
    } else if (num == 0) {

        System.out.println("The integer is zero");
    } else {

        System.out.println("The integer is positive");
    }
}

We read a value from the user and test whether it is negative, zero, or positive.

String input = IO.readln("Enter an integer: ");
int num = Integer.parseInt(input.trim());

The IO.readln method prints a prompt and reads a line from the standard input. We convert the line to an integer with Integer.parseInt, which throws a NumberFormatException if the text is not a valid number.

if (num < 0) {

    System.out.println("The integer is negative");
} else if (num == 0) {

    System.out.println("The integer is zero");
} else {

    System.out.println("The integer is positive");
}

If the first condition is true, that is, the entered value is less than zero, the first block is executed and the remaining two blocks are skipped. Otherwise the condition following else if is checked. If it is true, the second block is executed. If neither condition is met, the block following else is executed.

$ java Main.java
Enter an integer: 4
The integer is positive
$ java Main.java
Enter an integer: 0
The integer is zero
$ java Main.java
Enter an integer: -3
The integer is negative

The conditional operator

When an if else statement only chooses between two values, the conditional (ternary) operator ?: is a more compact alternative. It is an expression, so its result can be assigned directly.

Main.java
void main() {

    int age = 17;

    String status = age >= 18 ? "adult" : "minor";
    System.out.println(status);
}

If the condition before ? is true, the expression evaluates to the value after ?; otherwise it evaluates to the value after :.

$ java Main.java
minor

The switch statement

The switch statement is a multi-way branch. It compares a value against a list of constants, each introduced by the case keyword, and executes the code of the matching case. The optional default label is executed if no other case matches. A switch is often clearer than a long chain of if and else if statements.

Main.java
void main() {

    String domain = IO.readln("Enter a domain: ").trim().toLowerCase();

    switch (domain) {

        case "us":
            System.out.println("United States");
            break;

        case "de":
            System.out.println("Germany");
            break;

        case "sk":
            System.out.println("Slovakia");
            break;

        case "hu":
            System.out.println("Hungary");
            break;

        default:
            System.out.println("Unknown");
            break;
    }
}

The user is asked to enter a country code top-level domain. The program prints the name of the matching country.

String domain = IO.readln("Enter a domain: ").trim().toLowerCase();

The trim method removes leading and trailing whitespace and toLowerCase converts the characters to lowercase. This way, "us", "US", and " us " all match the same case.

switch (domain) {
    ...
}

The value to be tested is placed in round brackets after the switch keyword. With constant case labels, it can be of type char, byte, short, int, their wrapper classes, String, or an enum type. With pattern matching, shown later, any reference type can be used.

case "us":
    System.out.println("United States");
    break;

If domain equals "us", the message is printed. The break statement then exits the switch. Without it, execution would fall through to the statements of the next case, which is a common source of bugs.

default:
    System.out.println("Unknown");
    break;

The default section is executed if none of the cases matches.

$ java Main.java
Enter a domain: us
United States

Fall-through is occasionally used on purpose to share code among several cases.

Main.java
void main() {

    int month = 4;

    switch (month) {

        case 12:
        case 1:
        case 2:
            System.out.println("Winter");
            break;

        case 3:
        case 4:
        case 5:
            System.out.println("Spring");
            break;

        default:
            System.out.println("Summer or autumn");
    }
}

The empty cases 3 and 4 fall through to the statements of case 5.

$ java Main.java
Spring

Arrow labels

Since Java 14, case labels can use the arrow form case ... ->. An arrow case executes only the expression, block, or throw statement on its right side; there is no fall-through, so no break is needed. Several constants can share one case, separated by commas.

Main.java
void main() {

    String domain = IO.readln("Enter a domain: ").trim().toLowerCase();

    switch (domain) {

        case "us" -> System.out.println("United States");
        case "de" -> System.out.println("Germany");
        case "sk" -> System.out.println("Slovakia");
        case "hu" -> System.out.println("Hungary");
        default -> System.out.println("Unknown");
    }
}

This is the domain example rewritten with arrow labels. It is still a switch statement; it only performs actions and produces no value.

$ java Main.java
Enter a domain: sk
Slovakia

The switch expression

A switch expression evaluates to a value, which can be assigned to a variable, returned, or passed to a method. Unlike a switch statement, a switch expression must be exhaustive: every possible input value must be covered, usually with a default case.

Main.java
void main() {

    String domain = IO.readln("Enter a domain: ").trim().toLowerCase();

    String country = switch (domain) {

        case "us" -> "United States";
        case "de" -> "Germany";
        case "sk" -> "Slovakia";
        case "hu" -> "Hungary";
        default -> "Unknown";
    };

    System.out.println(country);
}

Each case now yields a string, and the result of the whole switch is assigned to the country variable. Note the semicolon after the closing curly bracket; it ends the assignment statement.

$ java Main.java
Enter a domain: hu
Hungary
$ java Main.java
Enter a domain: xx
Unknown

When a case needs more than a single expression, we use a block and return the value with the yield statement.

Main.java
import java.time.DayOfWeek;
import java.time.LocalDate;

void main() {

    DayOfWeek day = LocalDate.of(2026, 10, 13).getDayOfWeek();

    String type = switch (day) {

        case SATURDAY, SUNDAY -> "weekend";
        case FRIDAY -> {
            System.out.println("Almost there...");
            yield "workday";
        }
        default -> "workday";
    };

    System.out.printf("%s is a %s%n", day, type);
}

We switch on an enum value. The SATURDAY and SUNDAY constants share one case. Enum constants in case labels are written without the type name.

$ java Main.java
TUESDAY is a workday

Pattern matching in switch

Since Java 21, case labels can contain type patterns and record patterns. A case matches if the value has the given type, and the pattern variables are bound to the value or its components. An optional when clause, called a guard, adds an extra condition. Combined with a sealed interface, the compiler knows all possible types and can check exhaustiveness without a default case.

Main.java
sealed interface Shape permits Circle, Square, Rectangle {}

record Circle(double radius) implements Shape {}
record Square(double side) implements Shape {}
record Rectangle(double width, double height) implements Shape {}

String describe(Shape shape) {

    return switch (shape) {
        case Circle c -> "circle, area %.2f".formatted(Math.PI * c.radius() * c.radius());
        case Square s -> "square, area %.2f".formatted(s.side() * s.side());
        case Rectangle(double w, double h) when w == h -> "rectangle with equal sides";
        case Rectangle(double w, double h) -> "rectangle, area %.2f".formatted(w * h);
    };
}

void main() {

    List<Shape> shapes = List.of(new Circle(1.5), new Square(3),
            new Rectangle(2, 5), new Rectangle(4, 4));

    for (Shape shape : shapes) {
        System.out.println(describe(shape));
    }
}

The describe method returns a different description for each kind of shape.

case Circle c -> "circle, area %.2f".formatted(Math.PI * c.radius() * c.radius());

The type pattern Circle c matches if the shape is a Circle; the c variable then holds the shape as a Circle, so no cast is needed.

case Rectangle(double w, double h) when w == h -> "rectangle with equal sides";
case Rectangle(double w, double h) -> "rectangle, area %.2f".formatted(w * h);

The record pattern Rectangle(double w, double h) deconstructs the record into its components. The first case is guarded, so it only matches rectangles with equal sides. Cases are tested from top to bottom, which is why the guarded case must come before the general one.

$ java Main.java
circle, area 7.07
square, area 9.00
rectangle, area 10.00
rectangle with equal sides

The while statement

The while statement executes a block repeatedly as long as a boolean condition is true. The condition is tested before each iteration, so the body may not run at all.

while (expression) {

    statement;
}
Main.java
void main() {

    int i = 0;
    int sum = 0;

    while (i < 10) {

        i++;
        sum += i;
    }

    System.out.println(sum);
}

The example calculates the sum of the integers from 1 to 10.

A counting while loop typically has three parts: initialization, testing, and updating. Each execution of the body is called an iteration.

int i = 0;

We initialize the i variable. It is used as a counter.

while (i < 10) {
    ...
}

The expression inside the round brackets is the test. The statements in the body are executed until the expression evaluates to false.

i++;

In the update phase, we increment the counter. Since the counter is incremented before it is added to the sum, the loop adds the values 1 to 10. Forgetting to update the counter leads to an infinite loop.

$ java Main.java
55

The do while statement

The do while statement tests the condition after the body is executed. Therefore, the body always runs at least once, even if the condition is false from the start.

Main.java
void main() {

    int count = 0;

    do {
        System.out.println(count);
    } while (count != 0);
}

The block is executed first, and then the condition is evaluated. In our case, the condition is false, so the loop terminates after a single iteration.

$ java Main.java
0

The for statement

When the number of iterations is known before the loop starts, the for statement is usually the best choice. It puts the initialization, the condition, and the update of a counter variable on one line.

Main.java
void main() {

    for (int i = 0; i < 10; i++) {

        System.out.print(i + " ");
    }

    System.out.println();
}

In this example, we print numbers 0 to 9 to the console.

for (int i = 0; i < 10; i++) {

    System.out.print(i + " ");
}

There are three phases in a for loop. First, the counter i is initialized to zero; this is done only once. Next comes the condition. If it is true, the body is executed. Then the counter is incremented. Phases two and three repeat until the condition is false. In our case, the loop ends when i reaches 10. The i variable is only visible inside the loop.

$ java Main.java
0 1 2 3 4 5 6 7 8 9

A for loop is commonly used to traverse an array. The length field of the array gives its size.

Main.java
void main() {

    String[] planets = { "Mercury", "Venus", "Earth", "Mars",
            "Jupiter", "Saturn", "Uranus", "Neptune" };

    for (int i = 0; i < planets.length; i++) {

        System.out.println(planets[i]);
    }

    System.out.println("In reverse:");

    for (int i = planets.length - 1; i >= 0; i--) {

        System.out.println(planets[i]);
    }
}

We have an array holding the names of the planets of the Solar System. Using two for loops, we print them in ascending and descending order.

for (int i = 0; i < planets.length; i++) {

    System.out.println(planets[i]);
}

Arrays use zero-based indexing; the first item has index 0. Therefore, the i variable is initialized to zero. The condition checks that i is less than the length of the array, and the update increments it.

for (int i = planets.length - 1; i >= 0; i--) {

    System.out.println(planets[i]);
}

This for loop prints the elements in reverse order. Since indexing is zero based, the last element has the index planets.length - 1, which is where the counter starts. The condition ensures that the counter is greater than or equal to zero, because array indexes cannot be negative. In the update phase, the counter is decremented.

$ java Main.java
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune
In reverse:
Neptune
Uranus
Saturn
Jupiter
Mars
Earth
Venus
Mercury

The initialization and update parts of a for loop can contain several expressions separated by commas.

Main.java
import java.util.Arrays;
import java.util.Random;

void main() {

    var r = new Random();

    int[] values = new int[10];
    int num;
    int sum = 0;

    for (int i = 0; i < values.length; i++, sum += num) {

        num = r.nextInt(10);
        values[i] = num;
    }

    System.out.println(Arrays.toString(values));
    System.out.println("The sum of the values is " + sum);
}

We fill an array with ten random numbers from 0 to 9 and calculate their sum.

for (int i = 0; i < values.length; i++, sum += num) {

    num = r.nextInt(10);
    values[i] = num;
}

The update part of the for loop has two expressions separated by a comma. The i counter is incremented and the current number is added to the sum variable. While this is legal, putting the summation into the body is usually easier to read.

$ java Main.java
[1, 2, 4, 7, 5, 4, 9, 7, 0, 2]
The sum of the values is 41

Enhanced for statement

The enhanced for statement, also called the for-each loop, simplifies traversing arrays and collections. It has no explicit counter. It goes through the elements of an array or of any object that implements Iterable one by one, and assigns the current element to the loop variable.

Main.java
void main() {

    String[] planets = { "Mercury", "Venus", "Earth", "Mars",
            "Jupiter", "Saturn", "Uranus", "Neptune" };

    for (String planet : planets) {

        System.out.println(planet);
    }
}

We use the enhanced for statement to go through an array of planets.

for (String planet : planets) {

    System.out.println(planet);
}

The planets array is the source we iterate over, and planet is the variable that holds the current element. Assigning a new value to planet does not change the array. When we need the index or want to modify the elements, we use the classic for loop.

$ java Main.java
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune

The break statement

The break statement terminates the innermost enclosing while, do while, for, or switch statement.

Main.java
import java.util.Random;

void main() {

    var random = new Random();

    while (true) {

        int num = random.nextInt(30);
        System.out.print(num + " ");

        if (num == 22) {

            break;
        }
    }

    System.out.println();
}

We define an endless while loop and use the break statement to get out of it. In each iteration, we print a random value from 0 to 29. When the value equals 22, the loop ends.

while (true) {
    ...
}

Placing true in the round brackets of the while statement creates an endless loop. The loop must be terminated from inside its body, so we must make sure the exit condition is eventually reached.

if (num == 22) {

    break;
}

When the randomly chosen value is equal to 22, the break statement is executed and the while loop is terminated.

$ java Main.java
23 12 0 4 13 16 6 12 11 9 24 23 23 19 15 26 3 3 27 28 25 3 3 25 6 22
$ java Main.java
23 19 29 27 3 28 2 2 26 0 0 24 17 4 7 12 8 20 22
$ java Main.java
15 20 10 25 2 19 26 4 13 21 15 21 21 24 3 22

The continue statement

The continue statement skips the rest of the current iteration and continues with the next one. It can be used in while, do while, and for loops. In a for loop, the update part is still executed before the next test.

In the following example, we print the odd numbers up to 20.

Main.java
void main() {

    int num = 0;

    while (num < 20) {

        num++;

        if (num % 2 == 0) {
            continue;
        }

        System.out.print(num + " ");
    }

    System.out.println();
}

We iterate through the numbers 1 to 20 with the while loop.

if (num % 2 == 0) {
    continue;
}

If the expression num % 2 returns 0, the number is even. The continue statement is executed and the rest of the iteration is skipped, so the number is not printed. Note that the counter is incremented at the top of the body; incrementing it after continue would create an endless loop.

$ java Main.java
1 3 5 7 9 11 13 15 17 19

Labeled break and continue

In nested loops, a plain break or continue affects only the innermost loop. To control an outer loop, we mark it with a label and use the label in the statement.

Main.java
void main() {

    int[][] grid = {
        { 1, 4, 7 },
        { 2, 5, 8 },
        { 3, 6, 9 }
    };

    int target = 5;

    outer:
    for (int row = 0; row < grid.length; row++) {

        for (int col = 0; col < grid[row].length; col++) {

            if (grid[row][col] == target) {

                System.out.printf("Found %d at [%d][%d]%n", target, row, col);
                break outer;
            }
        }
    }
}

We search a two-dimensional array for a value. Once the value is found, the break outer statement terminates both loops, not just the inner one. Similarly, continue outer would skip to the next iteration of the outer loop.

$ java Main.java
Found 5 at [1][1]

Source

Java Language Specification - Blocks, Statements, and Patterns

Java Control Flow statements - tutorial

In this article we have covered control flow in Java: the if, else, and switch statements, switch expressions and pattern matching, the while, do while, for, and enhanced for loops, and the break and continue statements.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Java tutorials.