Java programs become useful when they can make decisions.
Think about software you use every day.
A banking application may need to decide:
Is the account balance sufficient for this withdrawal?
An e-commerce application may need to decide:
Is the customer eligible for a discount?
A login system may need to decide:
Are the username and password valid?
An examination system may need to decide:
Did the student pass, fail, or earn distinction?
Until now, imagine that Java simply executed every statement from top to bottom.
System.out.println("Step 1");
System.out.println("Step 2");
System.out.println("Step 3");The flow is predictable:
Step 1
↓
Step 2
↓
Step 3But real applications cannot always execute every statement.
Sometimes one path should run.
Sometimes another path should run.
Sometimes nothing should happen.
Sometimes one of several possible paths must be selected.
That ability to control which statements execute and in what order they execute is the foundation of this chapter.
The supplied chapter specifically covers Control Flow, if, if-else, if-else-if, nested conditions, multiple conditions, switch, traditional and modern switch syntax, yield, break, fall-through, the ternary operator, and choosing between if and switch.
1. First Understand Program Flow#
Before learning if or switch, we need one small idea.
What is flow?#
A program consists of instructions.
Normally Java executes them sequentially.
public class Main {
public static void main(String[] args) {
System.out.println("Start");
System.out.println("Processing");
System.out.println("End");
}
}Output:
Start
Processing
EndThe execution flow is:
main() starts
↓
print "Start"
↓
print "Processing"
↓
print "End"
↓
main() finishesThis is called sequential execution.
Now imagine:
int balance = 500;
int withdrawal = 1000;Should the application always execute the withdrawal?
Obviously not.
It must first ask:
Is balance >= withdrawal?Now our program requires a decision.
That brings us to control flow.
2. Control Flow#
Simple Meaning#
Control flow is the order in which statements in a program are executed.
Without special control structures, statements normally execute from top to bottom.
Java gives us constructs that can change this flow.
For this chapter, the most important are:
Control Flow
│
├── Decision Making
│ ├── if
│ ├── if-else
│ ├── if-else-if
│ ├── nested if
│ ├── switch
│ └── ternary operator
│
└── Flow Transfer
└── breakLoops also control program flow, but they belong to another chapter.
3. The Boolean Condition Behind Decision Making#
Before we write an if, there is one concept we must understand.
Java needs a way to answer questions such as:
Is age at least 18?
Is balance greater than zero?
Is password valid?
Is user logged in?Such questions ultimately produce either:
trueor:
falseJava represents these values with the primitive type:
booleanExample:
boolean loggedIn = true;
boolean paymentCompleted = false;Comparison expressions also produce boolean values.
int age = 25;
boolean result = age >= 18;
System.out.println(result);Output:
trueHere:
age >= 18is itself a boolean expression.
That is exactly what an if statement needs.
4. Java Does Not Use “Truthy” and “Falsy” Values#
This is an important Java rule.
Some programming languages allow code such as:
if (1)or:
if ("hello")Java does not.
The condition of an if must evaluate to boolean.
This is valid:
int age = 25;
if (age >= 18) {
System.out.println("Adult");
}This is invalid:
int age = 25;
if (age) {
System.out.println("Adult");
}The second example does not compile because age is an int, not a boolean.
Rule#
Java if condition
↓
must evaluate to
↓
boolean
↓
true or false5. Why Do We Need if?#
Suppose we have:
int age = 16;
System.out.println("You can vote.");This program prints:
You can vote.But the statement is incorrect for a 16-year-old.
What do we really want?
IF age is at least 18
allow votingJava gives us exactly this structure.
6. if Statement#
Mental Model#
Think of if as a gate.
Condition
↓
┌─────────┐
│ true ? │
└────┬────┘
│
┌──────┴──────┐
true false
│ │
↓ ↓
Run block Skip blockSyntax#
if (condition) {
// Statements executed when condition is true
}The condition must evaluate to true or false.
7. First if Example#
public class VotingExample {
public static void main(String[] args) {
int age = 20;
if (age >= 18) {
System.out.println("You are eligible to vote.");
}
System.out.println("Program finished.");
}
}Output:
You are eligible to vote.
Program finished.Now change:
int age = 15;Output:
Program finished.Why?
Because:
age >= 18becomes:
15 >= 18which is:
falseTherefore Java skips the body of the if.
8. Execution Flow of if#
Consider:
int temperature = 35;
if (temperature > 30) {
System.out.println("It is hot.");
}
System.out.println("Done.");Execution:
temperature = 35
↓
temperature > 30?
↓
true
↓
print "It is hot."
↓
print "Done."If:
temperature = 20;then:
temperature = 20
↓
temperature > 30?
↓
false
↓
skip if block
↓
print "Done."Notice something important:
if controls only its own block.
Code after the block continues normally.
9. Curly Braces and if#
Java permits a single statement without braces.
if (age >= 18)
System.out.println("Adult");This compiles.
But consider:
if (age >= 18)
System.out.println("Adult");
System.out.println("Access granted");Only the first statement belongs to the if.
Java effectively sees:
if (age >= 18) {
System.out.println("Adult");
}
System.out.println("Access granted");This can create serious bugs.
Preferred Production Style#
Use braces.
if (age >= 18) {
System.out.println("Adult");
System.out.println("Access granted");
}Even when there is only one statement:
if (age >= 18) {
System.out.println("Adult");
}It is easier to maintain and harder to accidentally break later.
10. if with Boolean Variables#
You do not always need to write a comparison directly.
boolean loggedIn = true;
if (loggedIn) {
System.out.println("Welcome back.");
}This is already a boolean expression.
You sometimes see:
if (loggedIn == true) {
System.out.println("Welcome back.");
}It works, but is unnecessarily verbose.
Prefer:
if (loggedIn) {
System.out.println("Welcome back.");
}For the opposite condition:
if (!loggedIn) {
System.out.println("Please log in.");
}! means logical NOT.
true → false
false → true11. A New Problem Appears#
if works when we want:
If condition is true → do something.But consider a login application.
We want:
If password is correct
show dashboard
otherwise
show errorWith only if, we might write:
if (passwordCorrect) {
System.out.println("Login successful.");
}
if (!passwordCorrect) {
System.out.println("Invalid password.");
}It works, but both conditions represent two opposite sides of the same decision.
Java gives us a cleaner structure.
12. if-else#
Mental Model#
Condition
↓
┌──────────┐
│ true ? │
└────┬─────┘
│
┌─────────┴─────────┐
true false
│ │
↓ ↓
if block else block
│ │
└─────────┬──────────┘
↓
continue programSyntax#
if (condition) {
// Runs when condition is true
} else {
// Runs when condition is false
}Exactly one branch executes.
13. if-else Example#
public class LoginExample {
public static void main(String[] args) {
boolean passwordCorrect = false;
if (passwordCorrect) {
System.out.println("Login successful.");
} else {
System.out.println("Invalid password.");
}
}
}Output:
Invalid password.If:
boolean passwordCorrect = true;output becomes:
Login successful.The important rule is:
if condition true
↓
execute if
↓
skip else
if condition false
↓
skip if
↓
execute elseBoth cannot execute during one evaluation of the same if-else.
14. Practical Example — Even or Odd#
We want to determine whether an integer is even.
A number is even when its remainder after division by 2 is zero.
The remainder operator is:
%Example:
10 % 2produces:
0So:
public class EvenOddExample {
public static void main(String[] args) {
int number = 17;
if (number % 2 == 0) {
System.out.println("Even");
} else {
System.out.println("Odd");
}
}
}Output:
Odd15. = Versus ==#
This is one of the most important beginner mistakes.
= means assignment.
int age = 25;It means:
Put25intoage.
== means equality comparison for primitive values.
age == 25It asks:
Isageequal to25?
For boolean variables, this can cause confusing code.
boolean active = false;
if (active = true) {
System.out.println("Active");
}This compiles.
Why?
Because:
active = trueassigns true to active.
The assignment expression itself evaluates to true.
Therefore the body executes.
That is usually a logical bug.
Prefer:
if (active) {
System.out.println("Active");
}16. What If We Have More Than Two Choices?#
Suppose students receive grades:
90+ → A
80+ → B
70+ → C
60+ → D
below 60 → FAn if-else handles two branches.
But here we need several mutually exclusive branches.
We need another form.
17. if-else-if Ladder#
Syntax#
if (condition1) {
// Branch 1
} else if (condition2) {
// Branch 2
} else if (condition3) {
// Branch 3
} else {
// Fallback branch
}Java evaluates conditions from top to bottom.
The first true branch executes.
Then the rest of the ladder is skipped.
18. Grade Example#
public class GradeExample {
public static void main(String[] args) {
int marks = 84;
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");
}
}
}Output:
Grade BLet's trace it.
marks = 84
84 >= 90 ?
false
↓
84 >= 80 ?
true
↓
print Grade B
↓
STOP checking remaining branchesJava does not continue to test:
84 >= 70even though that would also be true.
Why?
Because an if-else-if ladder selects the first matching branch.
19. Order Matters in if-else-if#
Consider this incorrect logic:
int marks = 95;
if (marks >= 60) {
System.out.println("Grade D");
} else if (marks >= 70) {
System.out.println("Grade C");
} else if (marks >= 80) {
System.out.println("Grade B");
} else if (marks >= 90) {
System.out.println("Grade A");
}What happens?
The first condition:
marks >= 60is already true.
So output becomes:
Grade Deven though 95 should be Grade A.
Preferred Ordering for Threshold Conditions#
Usually check from most restrictive/highest boundary toward the broader/lower ones.
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");
}20. Boundary Values#
Decision-making bugs often appear at boundaries.
Suppose:
Adult = age 18 or aboveCorrect:
if (age >= 18) {
System.out.println("Adult");
}Incorrect for the requirement:
if (age > 18) {
System.out.println("Adult");
}With the second version:
age = 18does not pass.
Boundary operators matter:
> greater than
>= greater than or equal
< less than
<= less than or equal
== equal
!= not equalIn production systems, many bugs come from choosing the wrong boundary operator.
21. Input Validation Before Classification#
Suppose marks should legally be between 0 and 100.
This code:
int marks = 150;
if (marks >= 90) {
System.out.println("Grade A");
}prints:
Grade ABut 150 is invalid input.
A robust decision tree should validate first.
int marks = 150;
if (marks < 0 || marks > 100) {
System.out.println("Invalid marks");
} else 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");
}This introduces ||, which we will understand properly in the Multiple Conditions section.
22. Another Problem — A Decision Inside a Decision#
Imagine an ATM.
First:
Is the PIN correct?Only if the PIN is correct should we ask:
Is the requested amount within the balance?The second decision belongs inside the first decision.
This gives us nested if.
23. Nested if#
A nested if is simply an if inside another if, else if, or else block.
if (condition1) {
if (condition2) {
// Executes only when both conditions reach this point
}
}Example:
public class AtmExample {
public static void main(String[] args) {
boolean pinCorrect = true;
double balance = 5000;
double amount = 1200;
if (pinCorrect) {
if (amount <= balance) {
System.out.println("Withdrawal approved.");
} else {
System.out.println("Insufficient balance.");
}
} else {
System.out.println("Invalid PIN.");
}
}
}Output:
Withdrawal approved.24. Nested if Execution#
PIN correct?
│
├── no → Invalid PIN
│
└── yes
↓
amount <= balance?
│
├── yes → Withdrawal approved
│
└── no → Insufficient balanceThe inner if is not even evaluated unless Java first enters the outer block.
25. When Nested if Becomes Too Deep#
This is technically valid:
if (loggedIn) {
if (active) {
if (emailVerified) {
if (hasPermission) {
System.out.println("Access granted");
}
}
}
}But deep nesting makes code harder to read.
This is sometimes called the arrow anti-pattern because indentation keeps moving to the right.
if
if
if
if
business logicProduction code often benefits from:
- combining conditions when they represent one decision,
- guard clauses,
- extracting meaningful methods.
For example:
if (loggedIn && active && emailVerified && hasPermission) {
System.out.println("Access granted");
}Whether this is better depends on whether all conditions belong to the same logical decision.
26. Multiple Conditions#
Real requirements rarely depend on one condition.
For example:
Allow loan application only if age is at least 21 and monthly income is at least ₹30,000.
We need logical operators.
The most important are:
&& logical AND
|| logical OR
! logical NOT27. Logical AND — &&#
&& means:
Both sides must be true.
Truth table:
| Left | Right | Left && Right |
|---|---|---|
| true | true | true |
| true | false | false |
| false | true | false |
| false | false | false |
Example:
int age = 30;
double income = 50000;
if (age >= 21 && income >= 30000) {
System.out.println("Eligible to apply.");
}Both conditions are true.
Therefore the combined condition is true.
28. Logical OR — ||#
|| means:
At least one side must be true.
| Left | Right | Left || Right |
|---|---|---|
| true | true | true |
| true | false | true |
| false | true | true |
| false | false | false |
Example:
boolean admin = false;
boolean manager = true;
if (admin || manager) {
System.out.println("Access allowed.");
}Output:
Access allowed.Only one of the two conditions needs to be true.
29. Logical NOT — !#
! reverses a boolean.
!true → false
!false → trueExample:
boolean accountBlocked = false;
if (!accountBlocked) {
System.out.println("Transaction allowed.");
}Since:
accountBlockedis false:
!accountBlockedis true.
30. Combining Multiple Conditions#
int age = 28;
boolean verified = true;
boolean blocked = false;
if (age >= 18 && verified && !blocked) {
System.out.println("User can continue.");
}This means:
age at least 18
AND
verified
AND
not blockedEvery part must be true.
31. Parentheses Make Complex Conditions Clearer#
Consider:
if (premium || admin && active) {
System.out.println("Access granted");
}Java operator precedence applies.
&& is evaluated before ||.
So Java interprets it like:
if (premium || (admin && active)) {
System.out.println("Access granted");
}If your business rule is:
User must be active
AND
user must be either premium or adminyou need:
if ((premium || admin) && active) {
System.out.println("Access granted");
}These conditions mean different things.
In production code, explicit parentheses often improve readability even when precedence rules technically make them unnecessary.
32. Short-Circuit Evaluation#
This concept is extremely important.
Consider:
conditionA && conditionBIf conditionA is already false, the whole expression can never become true.
Therefore Java does not need to evaluate conditionB.
This behavior is called short-circuit evaluation.
For &&:
false && anything
↓
falseFor ||:
true || anything
↓
true33. Why Short-Circuiting Matters#
Suppose:
String name = null;This is dangerous:
if (name.length() > 0) {
System.out.println("Name provided");
}Calling:
name.length()when name is null throws NullPointerException.
Instead:
if (name != null && name.length() > 0) {
System.out.println("Name provided");
}Execution when name == null:
name != null
↓
false
↓
&& already knows final answer is false
↓
name.length() is NOT evaluatedTherefore we avoid the null dereference.
A more expressive modern style is:
if (name != null && !name.isEmpty()) {
System.out.println("Name provided");
}34. Order of Conditions Can Matter#
Correct:
if (name != null && !name.isEmpty()) {
System.out.println(name);
}Dangerous:
if (!name.isEmpty() && name != null) {
System.out.println(name);
}If name is null, Java evaluates:
!name.isEmpty()first.
The exception occurs before it ever checks:
name != nullRule#
When using short-circuiting for safety:
safe/precondition check first
↓
operation that depends on it second35. && Versus & With Booleans#
Java also permits:
&with boolean operands.
But it does not short-circuit.
Example:
boolean result = false & expensiveCheck();expensiveCheck() still executes.
With:
boolean result = false && expensiveCheck();the method is skipped.
Similarly:
|can operate on booleans without short-circuiting, while:
||short-circuits.
For normal boolean decision logic, && and || are generally the expected operators.
& and | also have bitwise meanings for integer types.
36. Multiple Separate if Statements vs if-else-if#
These two structures are not equivalent.
Separate if#
int number = 10;
if (number > 0) {
System.out.println("Positive");
}
if (number % 2 == 0) {
System.out.println("Even");
}Output:
Positive
EvenBoth conditions are independently evaluated.
if-else-if#
int number = 10;
if (number > 0) {
System.out.println("Positive");
} else if (number % 2 == 0) {
System.out.println("Even");
}Output:
PositiveOnce the first condition is true, the second is skipped.
Decision Rule#
Use separate if statements when multiple independent conditions may all need to execute.
Use if-else-if when the branches represent mutually exclusive alternatives and normally only one should execute.
37. A New Kind of Decision Problem#
Now consider a menu:
1 → Create Account
2 → Deposit
3 → Withdraw
4 → Check Balance
5 → ExitWe could write:
if (choice == 1) {
// ...
} else if (choice == 2) {
// ...
} else if (choice == 3) {
// ...
} else if (choice == 4) {
// ...
} else if (choice == 5) {
// ...
}This works.
But all conditions compare one expression against several exact values.
That is exactly the type of problem for which switch is useful.
38. switch#
A switch selects a branch based on the value of an expression.
Traditional mental model:
choice
↓
┌──────────┼──────────┐
↓ ↓ ↓
case 1 case 2 case 3
↓ ↓ ↓
action action action
...
↓
defaultTraditional syntax:
switch (expression) {
case value1:
// statements
break;
case value2:
// statements
break;
default:
// fallback
}39. First Traditional switch#
public class MenuExample {
public static void main(String[] args) {
int choice = 2;
switch (choice) {
case 1:
System.out.println("Create Account");
break;
case 2:
System.out.println("Deposit");
break;
case 3:
System.out.println("Withdraw");
break;
case 4:
System.out.println("Check Balance");
break;
case 5:
System.out.println("Exit");
break;
default:
System.out.println("Invalid choice");
}
}
}Output:
Deposit40. How Traditional switch Executes#
For:
choice = 2;conceptually:
switch(choice)
↓
Is it case 1?
↓ no
Is it case 2?
↓ yes
execute case 2
↓
break
↓
leave switch41. What is case?#
Each case identifies a possible matching value.
case 1:means:
If the switch selector matches 1, begin executing here.Example:
String role = "ADMIN";
switch (role) {
case "ADMIN":
System.out.println("Administrator");
break;
case "USER":
System.out.println("Normal user");
break;
default:
System.out.println("Unknown role");
}Output:
Administrator42. What is default?#
default is the fallback branch.
It executes when no matching case is selected.
int choice = 99;
switch (choice) {
case 1:
System.out.println("One");
break;
case 2:
System.out.println("Two");
break;
default:
System.out.println("Unknown");
}Output:
Unknowndefault is similar in purpose to the final else in an if-else-if ladder.
It is optional syntactically, although whether omitting it is appropriate depends on the requirement.
43. Which Types Can Traditional switch Use?#
Traditional Java switch supports integral-style types such as:
byteshortcharint- corresponding wrapper types
enumStringsince Java 7
It does not traditionally support selectors such as:
long
float
double
booleanExample with char:
char grade = 'A';
switch (grade) {
case 'A':
System.out.println("Excellent");
break;
case 'B':
System.out.println("Good");
break;
default:
System.out.println("Other grade");
}44. Case Labels Must Be Compatible Constants#
Traditional case labels are not arbitrary runtime conditions.
This is the kind of usage switch is designed for:
int day = 2;
switch (day) {
case 1:
System.out.println("Monday");
break;
case 2:
System.out.println("Tuesday");
break;
}It is not a replacement for range expressions such as:
age >= 18
salary > 50000
score >= 90Those usually fit if better.
45. Why Is break Used?#
Now we encounter another concept.
Suppose a matching case begins executing.
With traditional colon-style switch, Java does not automatically stop after that case.
It continues into following statements unless control leaves the switch.
break tells Java:
Stop this switch and continue after it.
Example:
switch (choice) {
case 1:
System.out.println("One");
break;
case 2:
System.out.println("Two");
break;
}When case 1 executes, break exits the switch before case 2's statements run.
46. break#
In this chapter, the important use is breaking out of traditional switch.
matched case
↓
execute statements
↓
break
↓
exit switch
↓
continue after switchExample:
int value = 1;
switch (value) {
case 1:
System.out.println("Matched");
break;
default:
System.out.println("Not matched");
}
System.out.println("After switch");Output:
Matched
After switch47. What Happens If We Forget break?#
Now we arrive at one of the most famous switch behaviors.
Consider:
int value = 1;
switch (value) {
case 1:
System.out.println("One");
case 2:
System.out.println("Two");
case 3:
System.out.println("Three");
default:
System.out.println("Other");
}Prediction moment:
What do you think prints?
Not only:
OneInstead:
One
Two
Three
OtherWhy?
Once Java matches case 1, execution begins there and continues sequentially through subsequent labels.
This behavior is called fall-through.
48. Fall-Through Behavior#
Traditional switch:
case matched
↓
run statements
↓
no break?
↓
continue into next case
↓
continue...Example:
int value = 2;
switch (value) {
case 1:
System.out.println("One");
case 2:
System.out.println("Two");
case 3:
System.out.println("Three");
break;
default:
System.out.println("Other");
}Output:
Two
ThreeJava begins at matching case 2.
Because there is no break after case 2, execution falls into case 3.
The break after case 3 then exits.
49. Intentional Fall-Through#
Fall-through is not always a bug.
Sometimes several values should perform the same action.
Traditional syntax:
int month = 2;
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("Other season");
}For month 2, output:
WinterHere cases 12, 1, and 2 intentionally share one block.
However, accidental fall-through is a common source of bugs.
50. Traditional Switch with Strings#
public class RoleExample {
public static void main(String[] args) {
String role = "ADMIN";
switch (role) {
case "ADMIN":
System.out.println("Full access");
break;
case "EDITOR":
System.out.println("Edit access");
break;
case "VIEWER":
System.out.println("Read-only access");
break;
default:
System.out.println("Unknown role");
}
}
}String matching in a switch uses string value equality semantics.
But there is an important edge case.
51. Null and Traditional Switch#
Consider:
String role = null;
switch (role) {
case "ADMIN":
System.out.println("Admin");
break;
default:
System.out.println("Unknown");
}In traditional Java switch semantics, switching on a null selector causes NullPointerException.
default does not mean:
Handle null automatically.
default means:
Handle a non-matching selector value.
If null is possible and your Java/version-specific switch form does not explicitly handle it, validate first.
if (role == null) {
System.out.println("Role is missing");
} else {
switch (role) {
case "ADMIN":
System.out.println("Admin");
break;
default:
System.out.println("Unknown");
}
}Later Java versions introduced pattern-matching enhancements to switch, including explicit null-related capabilities in appropriate syntax, but that is a separate advanced topic and should not be mixed into the basic traditional-switch model.
52. Another Limitation of Traditional switch#
Suppose we want to assign a description.
Traditional code might look like:
int day = 2;
String name;
switch (day) {
case 1:
name = "Monday";
break;
case 2:
name = "Tuesday";
break;
case 3:
name = "Wednesday";
break;
default:
name = "Unknown";
}This works.
But notice the repetition:
name =
break
name =
break
name =
breakWouldn't it be cleaner if the switch itself could produce a value?
Modern Java provides that ability.
53. Traditional Switch vs Switch Expression#
Traditional switch was historically a statement.
It primarily controlled execution.
Modern Java also provides switch expressions.
An expression produces a value.
For example:
int result = 10 + 20;10 + 20 produces the value 30.
Similarly, a switch expression can produce a value.
String dayName = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
default -> "Unknown";
};The selected branch produces the value assigned to dayName.
54. Java Version Note for Switch Expressions#
This distinction matters in interviews and real projects.
The modern switch-expression feature went through preview releases and became a permanent standard feature in Java 14.
Therefore code such as:
String result = switch (value) {
case 1 -> "One";
default -> "Other";
};should not be presented as Java 8-compatible syntax.
If your production project runs Java 8 or Java 11, this syntax is unavailable.
Always check the project's Java version.
55. Arrow Syntax#
Modern switch allows arrow labels:
case value -> resultExample:
public class ModernSwitchExample {
public static void main(String[] args) {
int day = 2;
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";
};
System.out.println(dayName);
}
}Output:
TuesdayNotice the semicolon after the closing brace:
};Why?
Because the switch expression is part of this assignment:
String dayName = ...;The assignment statement must end with ;.
56. Arrow Syntax Avoids Traditional Fall-Through#
Consider:
int value = 1;
switch (value) {
case 1 -> System.out.println("One");
case 2 -> System.out.println("Two");
case 3 -> System.out.println("Three");
default -> System.out.println("Other");
}Output:
OneJava does not continue into case 2 and case 3 in the traditional fall-through manner.
This makes many switch implementations safer and clearer.
57. Multiple Labels in Modern Switch#
Suppose Saturday and Sunday should both return "Weekend".
Modern syntax:
int day = 6;
String type = switch (day) {
case 1, 2, 3, 4, 5 -> "Weekday";
case 6, 7 -> "Weekend";
default -> "Invalid";
};
System.out.println(type);Output:
WeekendThis is cleaner than traditional intentional fall-through.
58. What If a Switch Expression Branch Needs Multiple Statements?#
Simple branch:
case 1 -> "One"But suppose a case needs:
- logging,
- calculation,
- then producing a value.
We cannot just place multiple statements after the arrow without a block.
We can use:
case 1 -> {
// statements
}But now we need a way to return the branch's value to the switch expression.
That is where yield appears.
59. yield#
yield provides a value from a block inside a switch expression.
Example:
public class YieldExample {
public static void main(String[] args) {
int score = 1;
String result = switch (score) {
case 1 -> {
System.out.println("Processing case 1");
yield "One";
}
case 2 -> {
System.out.println("Processing case 2");
yield "Two";
}
default -> {
System.out.println("Processing default");
yield "Unknown";
}
};
System.out.println(result);
}
}Output:
Processing case 1
OneInside:
case 1 -> {
System.out.println("Processing case 1");
yield "One";
}yield "One"; means:
The value produced by this branch is "One".60. yield Is Not the Same as return#
Suppose a switch expression is inside a method.
public static String getDescription(int value) {
return switch (value) {
case 1 -> {
System.out.println("Case 1");
yield "One";
}
default -> "Other";
};
}Here:
yield "One";provides a value to the switch expression.
Then:
return switch (...)returns the final value from the method.
Mental model:
yield
↓
gives value to switch expression
↓
switch expression evaluates
↓
return
↓
gives method result to callerThey operate at different levels.
61. break vs yield#
This is an excellent interview comparison.
| Aspect | break | yield |
|---|---|---|
| Primary purpose | Transfer control out | Produce a switch-expression value |
| Traditional switch | Common | Not used for this purpose |
| Switch expression block | Does not provide required result | Provides result |
| Produces branch value | No | Yes |
| Typical syntax | break; | yield value; |
Example traditional:
switch (value) {
case 1:
System.out.println("One");
break;
}Example expression block:
String result = switch (value) {
case 1 -> {
System.out.println("One");
yield "ONE";
}
default -> "OTHER";
};62. Switch Expression Must Produce a Value#
Consider:
String result = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
};There is a problem.
What should happen if:
day = 99;Because the switch expression must produce a result, its cases generally need to be exhaustive.
For simple integer cases, this usually means adding:
defaultExample:
String result = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
default -> "Unknown";
};Enums can sometimes allow exhaustiveness without a default when every possible enum constant is covered, depending on the switch form and compiler analysis.
The core idea is:
If a switch is being used as an expression, Java must know that execution can produce a value on every reachable selector path.
63. Traditional Switch Statement vs Modern Switch Expression#
| Dimension | Traditional Switch | Switch Expression |
|---|---|---|
| Typical syntax | case X: | often case X -> |
| Main role | Control flow | Can produce value |
| Fall-through | Possible with colon form | Arrow rules avoid normal fall-through |
break | Often needed | Not used to produce branch value |
yield | Not normally needed | Used for multi-statement value-producing block |
| Assignment | Usually assign inside cases | Switch itself can be assigned |
| Boilerplate | Often more | Usually less |
| Java availability | Older Java versions | Standard from Java 14 |
Traditional#
String type;
switch (day) {
case 1:
type = "Weekday";
break;
case 7:
type = "Weekend";
break;
default:
type = "Unknown";
}Modern#
String type = switch (day) {
case 1 -> "Weekday";
case 7 -> "Weekend";
default -> "Unknown";
};64. Arrow Labels Can Also Be Used Without Assignment#
Do not think -> automatically means:
This must be assigned somewhere.
Modern switch statement forms can also use arrow rules for side effects.
int choice = 1;
switch (choice) {
case 1 -> System.out.println("Create");
case 2 -> System.out.println("Update");
case 3 -> System.out.println("Delete");
default -> System.out.println("Invalid");
}There is no variable assignment here.
So distinguish:
modern arrow switch rulefrom:
switch expressionA switch expression is specifically being used where a value is expected.
65. Grouping Cases — Traditional vs Modern#
Traditional#
switch (day) {
case 6:
case 7:
System.out.println("Weekend");
break;
default:
System.out.println("Weekday");
}Modern#
switch (day) {
case 6, 7 -> System.out.println("Weekend");
default -> System.out.println("Weekday");
}The modern form communicates intent more directly.
66. Another Decision Tool — Ternary Operator#
Now imagine a very small decision.
If age >= 18
status = "Adult"
else
status = "Minor"Writing this is perfectly valid:
String status;
if (age >= 18) {
status = "Adult";
} else {
status = "Minor";
}But for a simple value selection, Java gives us a compact conditional operator.
It is commonly called the ternary operator.
67. Ternary Operator#
Syntax:
condition ? valueIfTrue : valueIfFalseExample:
int age = 20;
String status = age >= 18 ? "Adult" : "Minor";
System.out.println(status);Output:
AdultMental model:
age >= 18
↓
┌─────┴─────┐
true false
↓ ↓
"Adult" "Minor"
└─────┬───────┘
↓
status68. Why Is It Called Ternary?#
“Ternary” means the operator works with three operands.
condition ? value1 : value2The three parts are:
1. condition
2. value when true
3. value when false69. Ternary Is an Expression#
This matters.
An if statement controls execution.
A ternary expression produces a value.
Example:
int number = 10;
String type = number % 2 == 0 ? "Even" : "Odd";The expression:
number % 2 == 0 ? "Even" : "Odd"evaluates to either "Even" or "Odd".
70. Ternary in a Method Call#
Because it is an expression, it can appear where a value is expected.
int marks = 75;
System.out.println(marks >= 40 ? "Pass" : "Fail");Output:
PassThis is valid, although readability should still guide usage.
71. Ternary for Numeric Values#
int a = 10;
int b = 20;
int max = a > b ? a : b;
System.out.println(max);Output:
2072. Ternary Should Not Replace Complex if Logic#
This is technically possible:
String grade = marks >= 90
? "A"
: marks >= 80
? "B"
: marks >= 70
? "C"
: "D";But nested ternary expressions can quickly become difficult to read.
A clearer version may be:
String grade;
if (marks >= 90) {
grade = "A";
} else if (marks >= 80) {
grade = "B";
} else if (marks >= 70) {
grade = "C";
} else {
grade = "D";
}Or, depending on the exact problem and Java version, another approach may communicate the business rule better.
Practical Rule#
Use ternary for small, obvious value choices.
Do not use it merely to make code shorter.
Shorter code is not automatically better code.
73. Ternary and Side Effects#
Code such as this may compile:
boolean success = true;
System.out.println(success ? "Saved" : "Failed");Good.
But trying to pack complex side-effect-heavy behavior into ternary expressions usually harms readability.
Prefer normal control flow when branches perform several operations.
74. Choosing Between if and switch#
This is not a question of:
Which one is always better?
The answer depends on the shape of the decision.
75. Use if When Conditions Involve Ranges#
Example:
if (age < 13) {
System.out.println("Child");
} else if (age < 18) {
System.out.println("Teenager");
} else {
System.out.println("Adult");
}This naturally uses relational conditions.
Traditional switch is not designed around arbitrary conditions like:
age < 1376. Use if for Complex Boolean Logic#
Example:
if ((premium || admin) && active && !blocked) {
System.out.println("Access granted");
}This is a compound boolean rule.
if communicates it naturally.
77. Use switch for Discrete Value Selection#
Example:
switch (command) {
case "START":
start();
break;
case "STOP":
stop();
break;
case "PAUSE":
pause();
break;
default:
handleUnknownCommand();
}You are testing one selector against a fixed set of discrete alternatives.
That fits switch well.
78. Use Modern Switch Expressions for Value Mapping#
Example:
String label = switch (statusCode) {
case 200 -> "Success";
case 400 -> "Bad Request";
case 404 -> "Not Found";
case 500 -> "Server Error";
default -> "Other";
};This is clear because the switch maps:
input value
↓
output value79. Use Ternary for Very Small Two-Way Value Selection#
Example:
String label = active ? "Active" : "Inactive";This is clearer than:
String label;
if (active) {
label = "Active";
} else {
label = "Inactive";
}However, if each branch requires several statements, use if-else.
80. Decision Guide#
Do you need a decision?
↓
yes
↓
Is it a simple two-way value choice?
│
├── yes → consider ternary
│
└── no
↓
Does decision depend on ranges,
different variables, or complex boolean logic?
│
├── yes → if / else-if
│
└── no
↓
Are you matching one expression
against discrete constant-like values?
│
├── yes → switch
│
└── no → if is usually clearer81. Complete Comparison#
| Feature | if | if-else | if-else-if | switch | Ternary |
|---|---|---|---|---|---|
| Single condition | Excellent | Good | Possible | Usually unnecessary | Possible |
| Two branches | Possible | Excellent | Possible | Possible | Excellent for simple values |
| Many discrete values | Verbose | Verbose | Possible | Excellent | Poor |
| Ranges | Excellent | Excellent | Excellent | Usually poor fit | Simple only |
| Complex boolean logic | Excellent | Excellent | Excellent | Limited fit | Avoid when complex |
| Produces value directly | No | No | No | Modern switch can | Yes |
| Multiple statements per branch | Excellent | Excellent | Excellent | Excellent | Poor readability |
| Fall-through concern | No | No | No | Traditional syntax: yes | No |
82. Common Mistakes#
Mistake 1 — Using = Instead of Intended Boolean Comparison#
Risky#
boolean active = false;
if (active = true) {
System.out.println("Active");
}Why Developers Make It#
= and == look similar.
Consequence#
The program changes active to true, so the branch executes.
Preferred#
if (active) {
System.out.println("Active");
}Debugging Clue#
A boolean unexpectedly changes before the branch.
Interview Connection#
Interviewers may ask whether assignment is allowed inside an if condition.
For boolean assignment expressions, yes, because the expression itself produces a boolean result—but it is usually unintended and poor style.
Mistake 2 — Wrong Boundary Operator#
Requirement:
18 and aboveWrong:
if (age > 18) {
System.out.println("Eligible");
}Correct:
if (age >= 18) {
System.out.println("Eligible");
}Consequence:
Boundary value 18 behaves incorrectly.
Mistake 3 — Incorrect else-if Ordering#
Risky:
if (marks >= 40) {
System.out.println("Pass");
} else if (marks >= 75) {
System.out.println("Distinction");
}For 90, first condition already matches.
Preferred:
if (marks >= 75) {
System.out.println("Distinction");
} else if (marks >= 40) {
System.out.println("Pass");
} else {
System.out.println("Fail");
}Mistake 4 — Using Independent ifs When Branches Must Be Exclusive#
int marks = 95;
if (marks >= 40) {
System.out.println("Pass");
}
if (marks >= 75) {
System.out.println("Distinction");
}This may be correct if both messages are intended.
It is wrong only if the business requirement demands exactly one classification.
Then use:
if (marks >= 75) {
System.out.println("Distinction");
} else if (marks >= 40) {
System.out.println("Pass");
} else {
System.out.println("Fail");
}Mistake 5 — Missing Braces Creating Misleading Code#
Risky:
if (admin)
System.out.println("Admin");
System.out.println("Sensitive action");The second print is unconditional.
Preferred:
if (admin) {
System.out.println("Admin");
System.out.println("Sensitive action");
}Mistake 6 — Wrong Short-Circuit Order#
Dangerous:
if (!name.isEmpty() && name != null) {
System.out.println(name);
}If name is null, the method call happens first.
Preferred:
if (name != null && !name.isEmpty()) {
System.out.println(name);
}Mistake 7 — Accidentally Using & Instead of &&#
if (name != null & !name.isEmpty()) {
System.out.println(name);
}Both operands are evaluated.
Therefore name.isEmpty() may still execute when name is null.
Preferred:
if (name != null && !name.isEmpty()) {
System.out.println(name);
}Mistake 8 — Forgetting break in Traditional Switch#
Risky:
switch (choice) {
case 1:
System.out.println("Create");
case 2:
System.out.println("Update");
}For choice = 1, both messages execute.
Preferred when fall-through is not intentional:
switch (choice) {
case 1:
System.out.println("Create");
break;
case 2:
System.out.println("Update");
break;
}Or on a compatible modern Java version:
switch (choice) {
case 1 -> System.out.println("Create");
case 2 -> System.out.println("Update");
default -> System.out.println("Unknown");
}Mistake 9 — Assuming default Handles Null#
String role = null;
switch (role) {
default:
System.out.println("Unknown");
}Traditional string switch does not simply route null to default.
Null may cause NullPointerException.
Validate appropriately.
Mistake 10 — Using Switch for Range Logic#
Hard-to-read workaround logic should not be forced into switch just because switch exists.
For ranges:
if (score >= 90) {
// ...
} else if (score >= 80) {
// ...
}is generally more natural.
Mistake 11 — Deeply Nested Conditions#
Risky for maintainability:
if (user != null) {
if (user.isActive()) {
if (user.hasPermission()) {
if (requestIsValid) {
process();
}
}
}
}When appropriate, restructure the logic.
For example:
if (user == null) {
return;
}
if (!user.isActive()) {
return;
}
if (!user.hasPermission()) {
return;
}
if (!requestIsValid) {
return;
}
process();These early exits are often called guard clauses.
The exact approach depends on surrounding method semantics.
Mistake 12 — Nested Ternary Abuse#
Risky:
String result = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "D";It may be syntactically valid but harder to scan and maintain.
Prefer an if-else-if structure when conditions become non-trivial.
83. Edge Cases and Traps#
Null Values#
Check nullable references before dereferencing them.
if (name != null && !name.isEmpty()) {
// safe to use name here
}Empty Strings#
Null and empty are different.
String a = null;
String b = "";For b:
b.isEmpty()is valid and returns true.
For a:
a.isEmpty()throws NullPointerException.
Boundary Numbers#
Always test values:
just below boundary
exactly boundary
just above boundaryFor:
age >= 18test:
17
18
19Invalid Input#
Classification logic should not silently treat invalid input as valid.
if (marks < 0 || marks > 100) {
System.out.println("Invalid");
}Duplicate Case Labels#
A switch cannot contain duplicate case labels for the same constant value.
This does not compile:
switch (value) {
case 1:
System.out.println("A");
break;
case 1:
System.out.println("B");
break;
}Traditional Fall-Through#
Always determine whether fall-through is intentional.
If intentional, make the structure obvious.
If not intentional, use break or modern arrow syntax where available.
84. Internal Working Mental Model#
At the language level, your most useful mental model is not JVM bytecode trivia.
Think in decision steps.
if#
evaluate boolean condition
↓
true? ──yes──→ execute branch
│
no
↓
skip branchif-else-if#
condition 1
│
true → branch 1 → done
│
false
↓
condition 2
│
true → branch 2 → done
│
false
↓
...
↓
elseTraditional switch#
evaluate selector once
↓
find matching case
↓
begin execution there
↓
continue sequentially
↓
break / end of switchArrow Switch#
evaluate selector
↓
select matching rule
↓
execute only that ruleSwitch Expression#
evaluate selector
↓
select branch
↓
branch produces value
↓
switch expression has result
↓
result used by surrounding expression85. Performance Perspective#
Developers sometimes ask:
Isswitchfaster thanif?
That is usually the wrong starting point.
Compilers and the JVM can optimize control flow in different ways depending on selector type, value density, bytecode shape, runtime profiling, and other factors.
For normal business code, the primary decision should usually be:
Which structure represents the requirement clearly and correctly?Do not rewrite a readable three-branch if into a strange switch because of imagined nanosecond gains.
Performance optimization should be based on evidence and profiling when performance actually matters.
86. Production Perspective#
Decision-making code directly represents business rules.
That makes readability especially important.
Compare:
if (a && b && !c && (d || e)) {
process();
}with:
boolean eligibleCustomer = age >= 18 && accountActive;
boolean transactionAllowed = !blocked && (adminApproved || withinLimit);
if (eligibleCustomer && transactionAllowed) {
process();
}Depending on the context, meaningful intermediate variables may make the rule easier to review and test.
Production concerns include:
- correct boundaries,
- null handling,
- invalid-state handling,
- explicit business rules,
- avoiding unintended fall-through,
- readable condition names,
- testable branches,
- avoiding unnecessary nesting,
- compatibility with the project's Java version.
87. Testing Decision Logic#
For every important conditional rule, test all meaningful paths.
Suppose:
if (age >= 18 && verified) {
allow();
} else {
reject();
}Meaningful test cases include:
| Age | Verified | Expected |
|---|---|---|
| 17 | true | Reject |
| 18 | true | Allow |
| 19 | true | Allow |
| 18 | false | Reject |
| 17 | false | Reject |
The exact boundary value deserves special attention.
88. Real-World Example — E-Commerce Discount#
Requirement:
Premium customer:
20% discount
Regular customer spending at least 5000:
10% discount
Others:
no discountImplementation:
public class DiscountExample {
public static void main(String[] args) {
boolean premium = false;
double orderAmount = 6000;
double discountPercentage;
if (premium) {
discountPercentage = 20;
} else if (orderAmount >= 5000) {
discountPercentage = 10;
} else {
discountPercentage = 0;
}
double discountAmount = orderAmount * discountPercentage / 100;
double finalAmount = orderAmount - discountAmount;
System.out.println("Discount: " + discountPercentage + "%");
System.out.println("Final amount: " + finalAmount);
}
}Output:
Discount: 10.0%
Final amount: 5400.089. Real-World Example — Permission Check#
public class PermissionExample {
public static void main(String[] args) {
boolean authenticated = true;
boolean admin = false;
boolean manager = true;
boolean blocked = false;
if (authenticated && !blocked && (admin || manager)) {
System.out.println("Sensitive report access granted.");
} else {
System.out.println("Access denied.");
}
}
}Output:
Sensitive report access granted.Read the condition in English:
authenticated
AND
not blocked
AND
(admin OR manager)Turning a boolean expression back into plain language is an excellent debugging technique.
90. Real-World Example — Order Status with Modern Switch#
Assume Java 14+.
public class OrderStatusExample {
public static void main(String[] args) {
String status = "SHIPPED";
String message = switch (status) {
case "NEW" -> "Order received";
case "PAID" -> "Payment completed";
case "PACKED" -> "Order packed";
case "SHIPPED" -> "Order is on the way";
case "DELIVERED" -> "Order delivered";
case "CANCELLED" -> "Order cancelled";
default -> "Unknown order status";
};
System.out.println(message);
}
}Output:
Order is on the wayThis is a strong switch use case because one value is being mapped to one of several discrete results.
91. Real-World Example — Validation with Guard Clauses#
public static void processWithdrawal(
boolean authenticated,
double amount,
double balance) {
if (!authenticated) {
System.out.println("Authentication required.");
return;
}
if (amount <= 0) {
System.out.println("Amount must be positive.");
return;
}
if (amount > balance) {
System.out.println("Insufficient balance.");
return;
}
System.out.println("Withdrawal approved.");
}Why may this be easier than deep nesting?
Because each invalid situation is rejected early.
The happy path remains simple:
System.out.println("Withdrawal approved.");92. Revision — One-Line Definitions#
Control Flow: The order in which program statements execute.
if: Executes a block only when a boolean condition is true.
if-else: Chooses between two mutually exclusive branches.
if-else-if: Checks multiple conditions in order and executes the first matching branch.
Nested if: An if contained inside another conditional branch.
Multiple Conditions: Boolean expressions combined using logical operators such as &&, ||, and !.
switch: Selects a branch based on one selector value.
Traditional Switch: Colon-style switch where execution can fall through until a break or switch end.
Switch Expression: A switch form capable of producing a value.
Arrow Syntax: Modern case ... -> switch rule syntax.
yield: Produces a value from a block within a switch expression.
break: Transfers control out of the current switch in the context discussed here.
Fall-Through: Traditional switch behavior where execution continues into subsequent case bodies when not stopped.
Ternary Operator: Compact conditional expression: condition ? trueValue : falseValue.
93. Syntax Revision#
if#
if (condition) {
// statements
}if-else#
if (condition) {
// true branch
} else {
// false branch
}if-else-if#
if (condition1) {
// branch 1
} else if (condition2) {
// branch 2
} else {
// fallback
}Traditional Switch#
switch (value) {
case 1:
// statements
break;
default:
// fallback
}Modern Switch Expression#
String result = switch (value) {
case 1 -> "One";
case 2 -> "Two";
default -> "Other";
};yield#
String result = switch (value) {
case 1 -> {
System.out.println("Processing");
yield "One";
}
default -> "Other";
};Ternary#
String result = condition ? "Yes" : "No";94. If You Remember Only 10 Things#
- Every Java
ifcondition must evaluate toboolean. if-elseselects exactly one of two branches.- An
if-else-ifladder stops at the first true branch. - Order matters when conditions overlap.
&&and||short-circuit.- Put prerequisite safety checks before expressions that depend on them.
- Traditional switch can fall through when
breakis missing. - Modern arrow switch rules avoid ordinary traditional fall-through.
- A switch expression can produce a value;
yieldprovides the value from a multi-statement block. - Choose the control structure that best represents the business rule, not the one that merely produces the shortest code.
95. Final Knowledge Map#
Decision Making and Control Flow
│
├── Foundation
│ ├── Sequential execution
│ ├── Control flow
│ ├── boolean
│ └── boolean expressions
│
├── if Family
│ ├── if
│ ├── if-else
│ ├── if-else-if
│ ├── ordering
│ ├── boundaries
│ └── nested if
│
├── Multiple Conditions
│ ├── &&
│ ├── ||
│ ├── !
│ ├── precedence
│ ├── parentheses
│ └── short-circuit evaluation
│
├── switch
│ ├── selector
│ ├── case
│ ├── default
│ ├── traditional switch
│ ├── break
│ └── fall-through
│
├── Modern Switch
│ ├── switch expression
│ ├── arrow syntax
│ ├── multiple labels
│ ├── exhaustive value production
│ └── yield
│
├── Conditional Expression
│ └── ternary operator
│
├── Decision Rules
│ ├── ranges → if
│ ├── complex boolean logic → if
│ ├── discrete selector values → switch
│ └── simple two-way value → ternary
│
├── Production Concerns
│ ├── boundaries
│ ├── null safety
│ ├── readability
│ ├── fall-through
│ ├── validation
│ ├── testing
│ └── Java-version compatibility
│
└── Interview
├── execution flow
├── short-circuiting
├── fall-through
├── break vs yield
├── switch statement vs expression
└── if vs switch