Control Flow — if, else, switch, Ternary Operator, and Pattern Matching
In this tutorial, you will learn about Control Flow. We cover key concepts, practical examples, and best practices to help you master this topic.
Control flow statements in Java direct the order of execution based on conditions, with if/else, switch expressions, the ternary operator, and pattern matching. Without control flow, every program would execute line-by-line from start to finish — control flow gives programs the ability to make decisions, react to data, and branch into different execution paths.
What You'll Learn
if/elsechains and best practicesswitchstatements vsswitchexpressions (Java 14+)- Ternary operator for compact conditionals
- Pattern matching for
instanceof(Java 16+) andswitch(Java 17+, preview)
Why It Matters
Control flow is the backbone of logic in every program. Choosing the right construct — if for complex conditions, switch for many discrete values, ternary for simple assignments — makes code more readable and less error-prone.
Real-World Use
A REST API controller uses if/else for validation, a shopping cart uses switch to apply different discount codes, and a configuration parser uses pattern matching to handle different JSON node types.
The if Statement
The simplest form tests a single condition:
int temperature = 30;
if (temperature > 25) {
System.out.println("It is a hot day.");
}
Java requires parentheses around the condition and braces for the body. The braces are optional for single statements but strongly recommended for readability and maintenance.
if/else and else if
int score = 85;
if (score >= 90) {
System.out.println("Grade: A");
} else if (score >= 80) {
System.out.println("Grade: B");
} else if (score >= 70) {
System.out.println("Grade: C");
} else {
System.out.println("Grade: F");
}
The conditions are evaluated top-down. Once one condition is true, the corresponding block executes and the rest are skipped.
The Ternary Operator
For simple binary assignments, the ternary operator (?:) is more concise:
int age = 20;
String status = (age >= 18) ? "Adult" : "Minor";
System.out.println(status); // Adult
Nested ternaries are possible but usually a bad idea:
String result = (a > b) ? "a > b" : (a < b) ? "a < b" : "equal";
Prefer if/else for more than one level of nesting.
switch Statement (Traditional)
int dayOfWeek = 3;
String dayName;
switch (dayOfWeek) {
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
case 4:
dayName = "Thursday";
break;
case 5:
dayName = "Friday";
break;
default:
dayName = "Weekend";
break;
}
Forgetting break causes fall-through: execution continues into the next case. This is a common source of bugs.
switch Expression (Java 14+)
The switch expression form is more concise and safer — no fall-through:
int dayOfWeek = 3;
String dayName = switch (dayOfWeek) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
case 4 -> "Thursday";
case 5 -> "Friday";
default -> "Weekend";
};
System.out.println(dayName); // Wednesday
Arrow (->) cases have no fall-through. If you need multiple statements per arm, use a block:
String result = switch (dayOfWeek) {
case 1 -> {
System.out.println("First day");
yield "Monday";
}
default -> "Unknown";
};
The yield keyword returns a value from a block arm. Switch expressions must be exhaustive — you must cover all possible values or include a default.
Switching on Enums
enum Color { RED, GREEN, BLUE }
Color c = Color.RED;
String hex = switch (c) {
case RED -> "#FF0000";
case GREEN -> "#00FF00";
case BLUE -> "#0000FF";
// no default needed: all cases covered
};
Pattern Matching for instanceof (Java 16+)
The old pattern required a separate variable declaration after instanceof:
// Old way
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
With pattern matching, the variable is declared inline:
// New way (Java 16+)
if (obj instanceof String s) {
System.out.println(s.length());
}
The variable s is scoped to the if block and is only accessible if the instanceof check succeeds.
Pattern Matching for switch (Java 17+, Preview in 17, Standardized in 21)
Object obj = "Hello";
String result = switch (obj) {
case Integer i -> "Integer: " + i;
case String s -> "String of length " + s.length();
case null -> "null value";
default -> "Unknown type";
};
Pattern matching in switch allows you to match both the type and optionally add a guard (when clause):
Object obj = 42;
String result = switch (obj) {
case Integer i when i > 0 -> "Positive integer: " + i;
case Integer i -> "Non-positive integer: " + i;
case String s -> "String: " + s;
default -> "Other";
};
Pattern matching eliminates the tedium of cascading if/else if chains with instanceof checks.
Common Mistakes
- Forgetting
breakin traditionalswitch. This causes fall-through. Use switch expressions (arrow syntax) to avoid this entirely. - Using
=instead of==in conditions.if (x = 5)does not compile in Java (unlike C/C++) because it requires a boolean. - Omitting curly braces for single-statement blocks. This leads to bugs when adding lines later:
if (condition) doSomething(); // only this is conditional doSomethingElse(); // always executes
- Not covering all cases in a switch expression. Switch expressions must be exhaustive. The compiler enforces this.
- Using
switchonnull. TraditionalswitchthrowsNullPointerExceptionif the selector isnull. Modern switch (pattern matching) can handlenullwithcase null.
Practice Questions
1. What is the difference between switch statement and switch expression?
A statement performs actions; an expression produces a value. Switch expressions use -> (no fall-through), require exhaustiveness, and use yield to return values from blocks.
2. What does this code print?
int x = 5;
if (x = 10) {
System.out.println("x is 10");
}
It does not compile. x = 10 is an assignment, not a comparison, and it produces an int, not a boolean.
3. How does pattern matching in switch differ from instanceof in if?
instanceof in if checks one type at a time. Switch pattern matching can check multiple types in separate cases, including null handling and guard conditions.
4. Why does this code produce a null pointer?
Integer val = null;
switch (val) {
case 1 -> System.out.println("one");
}
Traditional switch unboxes the Integer to int, causing a NullPointerException. Use a default case or pattern matching with case null.
5. Is the ternary operator always preferable to if/else?
No. Ternary is best for simple assignments. For complex logic or side effects, use if/else for clarity.
Challenge Question:
Write a program that uses pattern matching in switch to Process a list of mixed objects — String, Integer, Double, and null. For each, print a formatted description. Use a guard to handle negative numbers differently.
FAQ
Mini Project
Write a program Calculator.java that:
- Reads two numbers and an operator (
+,-,*,/,%) from command-line arguments - Uses a switch expression to perform the operation
- Handles division by zero with an
ifcheck - Uses pattern matching with
instanceofto validate that arguments are numeric - Formats output with
printf
Example run: java Calculator 10 + 3 should print 10 + 3 = 13.
Test edge cases: division by zero, non-numeric input, and missing arguments.
What's Next
Control flow lets you make decisions, but real programs need repetition. Lesson 7 covers loops — for, while, do-while, and the enhanced for-each loop — along with break, continue, and labeled loops for fine-grained flow control.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro