if
, else if
, and else
Conditional branching is a fundamental control flow structure in Java that allows the program to execute different code blocks based on conditions.
The if
statement checks a boolean condition. If true
, its block executes. Optionally, you can add an else if
to check additional conditions, and an else
block as a fallback.
int score = 75;
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");
}
true
condition's block executes.else
block executes if present.if
StatementsYou can nest if
inside another if
to check complex conditions:
int age = 20;
boolean hasLicense = true;
if (age >= 18) {
if (hasLicense) {
System.out.println("Allowed to drive.");
} else {
System.out.println("Needs a license.");
}
} else {
System.out.println("Too young to drive.");
}
public class IfStatementDemo {
public static void main(String[] args) {
// Example 1: Grading using if-else if-else
int score = 75;
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");
}
// Example 2: Nested if statements
int age = 20;
boolean hasLicense = true;
if (age >= 18) {
if (hasLicense) {
System.out.println("Allowed to drive.");
} else {
System.out.println("Needs a license.");
}
} else {
System.out.println("Too young to drive.");
}
}
}
Proper indentation and clear structure make if-else
logic easy to follow and maintain. Omitting an else
may cause missed cases, leading to unexpected behavior. Be cautious with overlapping conditions and always test edge cases. Using if-else if-else
correctly ensures your program handles all possibilities gracefully.
switch
Statements and FallthroughThe switch
statement provides a clean way to execute different code blocks based on the value of a variable, often used as an alternative to long if-else if
chains when testing one variable against multiple possible values.
switch (variable) {
case value1:
// code block
break;
case value2:
// code block
break;
default:
// code block executed if no case matches
}
switch
expression evaluates once.case
.break
statement prevents the program from continuing ("falling through") to the next case.default
case runs if no matching case is found; it's optional but recommended.break
int day = 3;
String dayName;
switch (day) {
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
default:
dayName = "Invalid day";
}
System.out.println(dayName); // Output: Wednesday
If you omit break
, execution continues into subsequent cases:
int month = 12;
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");
}
Here, months 12, 1, and 2 all print "Winter" because of fallthrough without intervening break
s.
public class SwitchDemo {
public static void main(String[] args) {
// Example 1: switch with break
int day = 3;
String dayName;
switch (day) {
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
default:
dayName = "Invalid day";
}
System.out.println("Day " + day + " is " + dayName); // Output: Wednesday
// Example 2: Intentional fallthrough
int month = 12;
System.out.print("Month " + month + " is in ");
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");
}
}
}
switch
is often cleaner and more readable than multiple if-else if
statements when testing a single variable against many discrete values. It can improve maintainability and reduce errors related to complex nested conditions. However, switch
works only with discrete values (like primitives, enums, and strings), while if
statements handle a broader range of conditions, including ranges and complex expressions. Being mindful of break
usage is crucial to prevent unexpected fallthrough bugs.
Starting with Java 14, the traditional switch
statement was enhanced with switch expressions, introducing a more concise and expressive way to handle multiple cases and return values directly.
Unlike the classic switch
statement, which is used primarily for control flow, switch expressions can produce a value. This means you can assign the result of a switch
expression directly to a variable.
int day = 3;
String dayName = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
default -> "Invalid day";
};
System.out.println(dayName); // Output: Wednesday
->
arrow replaces the colon (:
) and removes the need for explicit break
statements.switch
is an expression that evaluates to a result.default
case is required if not all possible cases are covered.Switch expressions support grouping multiple labels to share the same result:
String season = switch (month) {
case 12, 1, 2 -> "Winter";
case 3, 4, 5 -> "Spring";
default -> "Other";
};
For more complex logic, use a block with yield
to return a value:
String category = switch (score) {
case 90, 100 -> "Excellent";
case 80, 89 -> "Good";
default -> {
System.out.println("Score below 80");
yield "Needs Improvement";
}
};
public class SwitchExpressionDemo {
public static void main(String[] args) {
int day = 3;
String dayName = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
default -> "Invalid day";
};
System.out.println("Day " + day + " is " + dayName); // Output: Wednesday
int month = 12;
String season = switch (month) {
case 12, 1, 2 -> "Winter";
case 3, 4, 5 -> "Spring";
default -> "Other";
};
System.out.println("Month " + month + " is in " + season);
int score = 75;
String category = switch (score) {
case 90, 100 -> "Excellent";
case 80, 89 -> "Good";
default -> {
System.out.println("Score below 80");
yield "Needs Improvement";
}
};
System.out.println("Score " + score + " category: " + category);
}
}
Switch expressions simplify code by combining control flow and value production, reducing verbosity and errors like missing break
s. They make your logic clearer and more concise. However, since switch expressions require Java 14 or later, ensure your environment supports them before use. When used appropriately, switch expressions improve readability and maintainability in decision-making code.
instanceof
(Java 16)Traditionally, the instanceof
operator in Java is used to check if an object is an instance of a specific class or interface. However, this often requires an explicit type cast after the check, making the code verbose and repetitive.
instanceof
UsageObject obj = "Hello, World!";
if (obj instanceof String) {
String str = (String) obj; // explicit cast required
System.out.println(str.toUpperCase());
}
Here, after confirming obj
is a String
, you must cast it before using String
methods like toUpperCase()
.
instanceof
Java 16 introduced pattern matching for instanceof
, which combines the check and cast in one step:
Object obj = "Hello, World!";
if (obj instanceof String str) {
// str is automatically cast to String within this block
System.out.println(str.toUpperCase());
}
The variable str
is declared and initialized if the instanceof
check succeeds, eliminating the need for a separate cast.
public class InstanceofDemo {
public static void main(String[] args) {
Object obj1 = "Hello, World!";
Object obj2 = 123;
// Traditional instanceof usage with explicit cast
if (obj1 instanceof String) {
String str = (String) obj1; // explicit cast required
System.out.println("Traditional: " + str.toUpperCase());
}
// Pattern matching with instanceof (Java 16+)
if (obj2 instanceof String str) {
// This block won't execute because obj2 is not a String
System.out.println("Pattern matching: " + str.toUpperCase());
} else {
System.out.println("Pattern matching: obj2 is not a String");
}
}
}
if
block, reducing accidental misuse elsewhere.else
or nested conditions.Pattern matching with instanceof
streamlines common type-checking scenarios, making Java code more concise and expressive. It encourages safer coding practices by tying type checks directly to variable declarations, avoiding the boilerplate and risks associated with manual casting. As a result, developers write cleaner, easier-to-understand code when working with polymorphic types.
while
, do-while
, and for
LoopsLoops are essential in Java for executing code repeatedly based on a condition. Java provides three common loop types: while
, do-while
, and for
. Each serves specific use cases depending on when the condition should be checked and how the loop is structured.
while
LoopThe while
loop checks its condition before each iteration. If the condition is true
, the loop body runs; otherwise, it exits immediately.
int count = 1;
while (count <= 5) {
System.out.println("Count: " + count);
count++;
}
Use case: When the number of iterations is unknown and the loop may not run at all if the condition is initially false
.
do-while
LoopThe do-while
loop executes the loop body at least once before checking the condition at the end of each iteration.
int count = 1;
do {
System.out.println("Count: " + count);
count++;
} while (count <= 5);
Use case: When you need the loop body to run at least once regardless of the condition.
for
LoopThe for
loop is compact and ideal when the number of iterations is known or controlled by a counter.
for (int i = 1; i <= 5; i++) {
System.out.println("Count: " + i);
}
It has three parts:
int i = 1
— runs once before looping starts.i <= 5
— checked before each iteration.i++
— runs after each iteration.
public class LoopExamples {
public static void main(String[] args) {
System.out.println("While loop:");
int count1 = 1;
while (count1 <= 5) {
System.out.println("Count: " + count1);
count1++;
}
System.out.println("\nDo-while loop:");
int count2 = 1;
do {
System.out.println("Count: " + count2);
count2++;
} while (count2 <= 5);
System.out.println("\nFor loop:");
for (int i = 1; i <= 5; i++) {
System.out.println("Count: " + i);
}
}
}
while
when the loop should run only if a condition is true upfront.do-while
if the loop must run at least once before checking.for
when you have a clear iteration count or need a concise loop control structure.Choosing the appropriate loop improves readability and logic clarity. Misusing loops—like using while
when the body must run once—can cause bugs or unnecessarily complex code. Understanding these differences empowers you to write efficient, predictable loops in your Java programs.
for
Loop (for-each)The enhanced for
loop, also known as the for-each loop, provides a simplified syntax for iterating over arrays and collections without the need to manage an index variable explicitly.
int[] numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
System.out.println(num);
}
Here, num
takes on each value in the numbers
array, one by one, until the loop completes.
for
LoopThe equivalent traditional loop looks like this:
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]);
}
The enhanced for loop is more concise and eliminates boilerplate code such as index initialization, condition checking, and incrementing.
The for-each loop works similarly with any class implementing the Iterable
interface, such as ArrayList
:
List<String> names = List.of("Alice", "Bob", "Charlie");
for (String name : names) {
System.out.println(name);
}
import java.util.List;
public class ForEachDemo {
public static void main(String[] args) {
// Using enhanced for-each with an array
int[] numbers = {1, 2, 3, 4, 5};
System.out.println("Enhanced for-each with array:");
for (int num : numbers) {
System.out.println(num);
}
// Equivalent traditional for loop
System.out.println("\nTraditional for loop with array:");
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]);
}
// Using enhanced for-each with a List
List<String> names = List.of("Alice", "Bob", "Charlie");
System.out.println("\nEnhanced for-each with List:");
for (String name : names) {
System.out.println(name);
}
}
}
The enhanced for loop improves readability by focusing on elements rather than indices, reducing common mistakes like off-by-one errors. However, it does have limitations:
Despite these, the enhanced for loop is an excellent choice for simple, safe, and clear iteration over arrays and collections in Java.
break
, continue
, and LabelsJava provides control statements like break
and continue
to manage loop execution more precisely. These statements help alter the flow inside loops, and labeled statements extend this control especially in nested loops.
break
StatementThe break
statement immediately exits the nearest enclosing loop or switch
block:
for (int i = 1; i <= 5; i++) {
if (i == 3) {
break; // Exit loop when i is 3
}
System.out.println(i);
}
// Output: 1 2
Here, the loop terminates early when i
equals 3.
continue
StatementThe continue
statement skips the current iteration and proceeds to the next one:
for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue; // Skip printing when i is 3
}
System.out.println(i);
}
// Output: 1 2 4 5
The iteration where i
is 3 is skipped.
In nested loops, break
and continue
only affect the innermost loop by default. Labels allow you to specify which loop to control.
outerLoop:
for (int i = 1; i <= 3; i++) {
innerLoop:
for (int j = 1; j <= 3; j++) {
if (i == 2 && j == 2) {
break outerLoop; // Exit the outer loop entirely
}
System.out.println("i=" + i + ", j=" + j);
}
}
This code exits both loops when i
is 2 and j
is 2.
public class BreakContinueExample {
public static void main(String[] args) {
System.out.println("Break example:");
for (int i = 1; i <= 5; i++) {
if (i == 3) {
break; // Exit loop when i is 3
}
System.out.print(i + " ");
}
System.out.println("\n");
System.out.println("Continue example:");
for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue; // Skip printing when i is 3
}
System.out.print(i + " ");
}
System.out.println("\n");
System.out.println("Labeled break example:");
outerLoop:
for (int i = 1; i <= 3; i++) {
innerLoop:
for (int j = 1; j <= 3; j++) {
if (i == 2 && j == 2) {
break outerLoop; // Exit the outer loop entirely
}
System.out.println("i=" + i + ", j=" + j);
}
}
}
}
While break
and continue
can simplify complex looping logic by allowing early exits or skipping iterations, overusing them—especially with labeled loops—can make code harder to read and maintain. Use these statements judiciously and document their intent clearly to keep your code understandable and bug-free.