Skip to content
Chapter 22 · Core Java

Control Flow Statements in Java: Decision Making, Looping, and Branching ​

Every program you write needs to make decisions. Should this code run or not? Should you repeat this action ten times? Should you stop early if a condition is met? These are the kinds of questions that control flow statements answer. Without them, your program would just execute every line from top to bottom with no intelligence at all.

Control flow statements in Java fall into three broad categories:

  1. Decision making statements like if, if else, and switch
  2. Iterative statements like for, while, and do while loops
  3. Branching statements like break and continue

By the end of this article you will understand all three categories deeply, know the rules that Java enforces around them, and be prepared for the interview questions that come up around these topics constantly.


Decision Making Statements ​

The if Statement ​

Think about your morning routine. If it is raining outside, you grab an umbrella. That is an if statement in real life. You check a condition, and if that condition is true, you do something.

In Java, the structure looks like this:

java
int value = 13;

if (value > 8) {
    System.out.println("Value is greater than 8");
}

The condition inside the parentheses must evaluate to a boolean, either true or false. If it is true, the block of code inside the curly braces executes. If it is false, the block is skipped entirely and Java moves on to whatever comes after it.

The if else Statement ​

Now imagine you want to do one thing when the condition is true and a completely different thing when it is false. That is where else comes in.

java
int marks = 45;

if (marks >= 50) {
    System.out.println("You passed!");
} else {
    System.out.println("You failed. Try again.");
}

Only one of these two blocks will ever execute for any given value of marks. They are mutually exclusive.

The if else if Ladder ​

What if you have more than two possible outcomes? You chain conditions together using else if. Think about a grading system:

java
int marks = 72;

if (marks >= 90) {
    System.out.println("Grade: A");
} else if (marks >= 80) {
    System.out.println("Grade: B");
} else if (marks >= 70) {
    System.out.println("Grade: C");
} else if (marks >= 60) {
    System.out.println("Grade: D");
} else {
    System.out.println("Grade: F");
}

Java evaluates these conditions from top to bottom. The moment one condition is true, that block executes and all the remaining conditions are skipped. This is important: even if multiple conditions could theoretically be true, only the first matching one runs.

For marks = 72, the first condition (marks >= 90) is false, the second (marks >= 80) is false, and the third (marks >= 70) is true, so "Grade: C" prints and Java jumps past the rest.

Nested if Statements ​

You can place an if statement inside another if statement. This is called nesting. There is no limit to how deep you can nest, though deep nesting usually signals that your logic needs to be reorganized.

java
int value = 13;

if (value > 8) {
    // We are now inside the outer if block
    if (value < 15) {
        System.out.println("Value is greater than 8 but less than 15");
    } else {
        System.out.println("Value is greater than or equal to 15");
    }
}

Here value = 13 is greater than 8, so we enter the outer block. Inside that block, 13 is less than 15, so we enter the inner if and print the message. The inner else is skipped.


The Switch Statement ​

The switch statement is an alternative to a long chain of if else if conditions. It works especially well when you are comparing one variable against many specific constant values.

Think of a TV remote. You press a button (channel 1, 2, 3, etc.) and a specific action happens based on which button you pressed. That is exactly what switch does.

Basic Switch Syntax ​

java
int day = 3;

switch (day) {
    case 1:
        System.out.println("Monday");
        break;
    case 2:
        System.out.println("Tuesday");
        break;
    case 3:
        System.out.println("Wednesday");
        break;
    case 4:
        System.out.println("Thursday");
        break;
    default:
        System.out.println("Weekend or invalid day");
}

Java takes the value of day (which is 3) and checks it against each case value from top to bottom. When it finds case 3, it executes that block and prints "Wednesday".

Using an Expression as the Switch Value ​

You are not limited to passing a simple variable. You can pass an expression:

java
int a = 1;
int b = 2;

switch (a + b) {  // expression value is 3
    case 1:
        System.out.println("Sum is 1");
        break;
    case 2:
        System.out.println("Sum is 2");
        break;
    case 3:
        System.out.println("Sum is 3");
        break;
    default:
        System.out.println("Other sum");
}

Java evaluates a + b to get 3, then looks for case 3, finds it, and prints "Sum is 3".

Fall Through: One of the Most Important Switch Concepts ​

This is something that trips up beginners and comes up constantly in interviews. In Java, when a case matches and there is no break statement at the end of that case block, execution continues falling through into the next case block, and the next, and the next, until it either hits a break or reaches the end of the switch.

java
int value = 2;

switch (value) {
    case 1:
        System.out.println("Case 1");
    case 2:
        System.out.println("Case 2");  // matches here
    case 3:
        System.out.println("Case 3");  // falls through to here!
    case 4:
        System.out.println("Case 4");  // falls through to here!
        break;
    case 5:
        System.out.println("Case 5");
}

Output:

Case 2
Case 3
Case 4

This surprises people. Java matched case 2, but because there was no break, it kept executing case 3 and case 4 as well. Case 4 has a break, so execution stops there. Case 5 never runs.

Fall through is not always a bug. Sometimes it is intentional and useful. For example, grouping months into quarters:

java
String month = "February";

switch (month) {
    case "January":
    case "February":
    case "March":
        System.out.println("Month is in Quarter 1");
        break;
    case "April":
    case "May":
    case "June":
        System.out.println("Month is in Quarter 2");
        break;
    default:
        System.out.println("Other quarter");
}

Here January, February, and March all fall through to the same println statement. This is intentional grouping using fall through.

Switch Rules You Must Know ​

Java has strict rules about what you can and cannot use in a switch statement. These come up in every Java interview.

Allowed data types in the switch expression:

In the classic switch you can use exactly 10 types:

  • byte and its wrapper Byte
  • short and its wrapper Short
  • int and its wrapper Integer
  • char and its wrapper Character
  • String
  • enum

Not allowed in switch expressions:

  • long
  • float
  • double
  • boolean

(Since Java 21, pattern matching lets a switch work on any object type; see the pattern matching for switch chapter.)

Additional switch rules:

  • Two cases cannot have the same value. You cannot write case 3 twice in the same switch. The compiler will reject it.
  • Case values must be constants or literals, not variables. You can use a literal like case 5, or a final variable like case MY_CONSTANT, or an enum value, but you cannot use a regular variable like case x.
  • The default case is optional but highly recommended. It handles anything that does not match any case, like the else clause in an if else if ladder.
  • The default case does not have to be at the bottom. You can put it anywhere in the switch. Java will still evaluate it only when no other case matches.
java
final int MAX = 100;

switch (someValue) {
    case 1:          // literal, allowed
        break;
    case MAX:        // final variable, allowed (it's a compile-time constant)
        break;
    // case x:      // regular variable, NOT allowed
}

Switch Expression (Java 14 and Later) ​

Java 12 previewed, and Java 14 made standard, a modernized switch that works as an expression rather than just a statement. The big difference is the arrow -> syntax, which eliminates fall through entirely and makes the code much cleaner.

Arrow Syntax ​

java
int day = 3;

String dayName = switch (day) {
    case 1 -> "Monday";
    case 2 -> "Tuesday";
    case 3 -> "Wednesday";
    case 4 -> "Thursday";
    case 5 -> "Friday";
    case 6 -> "Saturday";
    case 7 -> "Sunday";
    default -> "Invalid day";
};

System.out.println(dayName);  // Wednesday

Notice several things:

  • The switch now returns a value, which you can assign to a variable.
  • Each case uses -> instead of :.
  • There is no break anywhere. Fall through cannot happen with arrow syntax.
  • All possible cases must be handled. If you do not cover every possible value, you must include a default.

Grouping Cases in Arrow Syntax ​

You can handle multiple values in one case using a comma separated list:

java
int month = 2;

String quarter = switch (month) {
    case 1, 2, 3 -> "Quarter 1";
    case 4, 5, 6 -> "Quarter 2";
    case 7, 8, 9 -> "Quarter 3";
    case 10, 11, 12 -> "Quarter 4";
    default -> "Invalid month";
};

System.out.println(quarter);  // Quarter 1

This is cleaner than the fall through grouping of the traditional switch.

The yield Statement ​

An arrow case can hold a single expression, or a whole block in { }. When a block must produce the switch's value, you return it with yield.

yield is used inside a switch expression to return a value from a block:

java
int value = 3;

String result = switch (value) {
    case 1 -> "One";
    case 2 -> "Two";
    case 3 -> {
        // You can write multiple lines here
        String msg = "The value is three";
        System.out.println("Computing result...");
        yield msg;  // yield returns the value from this block
    }
    default -> "Other";
};

System.out.println(result);

Think of yield as the return statement specifically for switch expressions. You use it when a case needs more than one line of logic before producing its value.


Iterative Statements (Loops) ​

Loops let you repeat a block of code multiple times without writing it out multiple times. Imagine printing numbers from 1 to 1000. You would not write System.out.println one thousand times. A loop does it in three lines.

The for Loop ​

The for loop is used when you know in advance how many times you want to iterate. It has three parts in its header: initialization, condition, and update.

java
for (int i = 1; i <= 5; i++) {
    System.out.println("Count: " + i);
}

Output:

Count: 1
Count: 2
Count: 3
Count: 4
Count: 5

How it works step by step:

  1. int i = 1 runs once at the start (initialization).
  2. i <= 5 is checked. If true, the body runs. If false, the loop ends.
  3. After the body runs, i++ executes (update).
  4. Go back to step 2 and repeat.

You can count backwards too:

java
for (int i = 10; i >= 1; i--) {
    System.out.println(i);
}

The while Loop ​

The while loop is used when you do not know in advance how many times you will iterate. The loop continues as long as the condition is true.

java
int value = 1;

while (value <= 5) {
    System.out.println(value);
    value++;
}

Output:

1
2
3
4
5

The variable is initialized before the loop. The condition is checked at the top. The update happens inside the body. The loop stops as soon as value becomes 6, because 6 <= 5 is false.

Important: With a while loop, if the condition is false from the very beginning, the loop body never executes at all. The body runs zero times.

The do while Loop ​

The do while loop is the one you use when you need the loop body to execute at least once, regardless of the condition.

java
int value = 10;

do {
    System.out.println("This always runs at least once: " + value);
    value++;
} while (value <= 5);

Output:

This always runs at least once: 10

Even though 10 <= 5 is false, the body still ran once because the condition is checked at the bottom, after the first execution. This is the key difference between do while and while.

A practical use case: a menu driven program. You always want to show the menu at least once, then keep showing it as long as the user does not choose to exit.

java
Scanner scanner = new Scanner(System.in);
int choice;

do {
    System.out.println("1. Play");
    System.out.println("2. Settings");
    System.out.println("3. Exit");
    System.out.print("Enter choice: ");
    choice = scanner.nextInt();
} while (choice != 3);

The for each Loop (Enhanced for Loop) ​

The for each loop is designed specifically for iterating over arrays and collections. It is cleaner and less error prone than a traditional for loop when you just want to go through every element.

java
int[] numbers = {10, 20, 30, 40, 50};

for (int num : numbers) {
    System.out.println(num);
}

Read this as: "for each num in numbers, print it."

You do not manage an index variable. You do not worry about going out of bounds. You just get each element in order.

java
String[] fruits = {"Apple", "Banana", "Cherry"};

for (String fruit : fruits) {
    System.out.println(fruit);
}

Output:

Apple
Banana
Cherry

The for each loop also works with any class that implements the Iterable interface, which includes all Java collections like ArrayList, HashSet, LinkedList, and so on.


Branching Statements ​

break ​

The break statement immediately exits the nearest enclosing loop or switch statement. Execution continues with the first statement after the loop or switch.

java
for (int value = 1; value <= 10; value++) {
    if (value == 5) {
        break;  // stop the loop when value reaches 5
    }
    System.out.println(value);
}

Output:

1
2
3
4

When value becomes 5, the break fires and the loop ends. 5 itself is never printed because the check happens before the print statement in this example. You can rearrange the order depending on your needs.

continue ​

The continue statement skips the rest of the current iteration and jumps to the next one. Unlike break, it does not exit the loop. The loop keeps going.

java
for (int value = 1; value <= 10; value++) {
    if (value == 3) {
        continue;  // skip 3, go to next iteration
    }
    System.out.println(value);
}

Output:

1
2
4
5
6
7
8
9
10

Number 3 is skipped. Everything else prints normally. The loop runs all 10 iterations, but when value == 3 the continue causes the println to be skipped for that iteration only.

break and continue in Nested Loops ​

When you have loops inside loops, break and continue by default affect only the innermost loop they are inside.

java
for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 3; j++) {
        if (j == 2) {
            break;  // only breaks the inner loop
        }
        System.out.println("i=" + i + ", j=" + j);
    }
}

Output:

i=1, j=1
i=2, j=1
i=3, j=1

The inner loop breaks when j == 2, but the outer loop keeps going through all three values of i. For each value of i, the inner loop runs, prints j=1, then breaks when j would become 2.

Similarly, continue in a nested loop only affects the innermost loop:

java
for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 3; j++) {
        if (j == 2) {
            continue;  // skips j=2 in the inner loop
        }
        System.out.println("i=" + i + ", j=" + j);
    }
}

Output:

i=1, j=1
i=1, j=3
i=2, j=1
i=2, j=3
i=3, j=1
i=3, j=3

j=2 is skipped for every value of i.

Labeled break: Breaking Out of Multiple Loops ​

What if you want to break out of the outer loop from inside a nested loop? By default break only exits the innermost loop. To break out of an outer loop you use a labeled break.

A label is an identifier followed by a colon that you place before the loop you want to target.

java
outer:  // this is the label for the outer loop
for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 3; j++) {
        if (i == 2 && j == 2) {
            break outer;  // breaks out of the outer loop
        }
        System.out.println("i=" + i + ", j=" + j);
    }
}
System.out.println("Exited both loops");

Output:

i=1, j=1
i=1, j=2
i=1, j=3
i=2, j=1
Exited both loops

When i==2 and j==2, the break outer does not just exit the inner loop. It exits the loop labeled outer, which is the outer for loop. Execution jumps to the line after that outer loop.

You can also use labeled continue to skip an iteration of an outer loop:

java
outer:
for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 3; j++) {
        if (j == 2) {
            continue outer;  // skips to next iteration of outer loop
        }
        System.out.println("i=" + i + ", j=" + j);
    }
}

Output:

i=1, j=1
i=2, j=1
i=3, j=1

When j == 2, instead of continuing the inner loop, continue outer skips to the next iteration of the outer loop. So j=2 and j=3 never print for any value of i.

Check yourself ​

  1. When does an if else if ladder stop checking conditions?
  2. What is fall through in a switch, and when is it useful?
  3. Which types can a classic switch use? Can two cases have the same value? Can a case use a normal variable?
  4. How does a switch expression differ from a switch statement? What does yield do?
  5. What is the difference between while and do while?
  6. What is the difference between break and continue? What does a labeled break do?
Answers
  1. At the first condition that is true; the rest are skipped.
  2. Without break, execution continues into the following cases. It is useful for grouping several cases that share one action, such as months of a quarter.
  3. byte, short, int, char, their wrappers, String, and enums (not long, float, double, boolean). No duplicate case values. Case labels must be constants (literals, final constants, or enum values).
  4. A switch expression produces a value, uses -> with no fall through, and must cover every value (usually with default). yield returns the value from a block inside a case.
  5. while checks the condition first, so the body may run zero times; do while runs the body once before checking.
  6. break exits the loop (or switch); continue skips to the next iteration. A labeled break exits the outer loop named by the label.