1Z0-830 Control Flow - Java SE 21 Certification Prep
If-Else Statements
Conditional statements allow your program to make decisions and execute different code paths based on boolean conditions. The if-else statement is the fundamental building block of decision-making in Java.
// Basic if statement - executes block only if condition is true
int score = 85;
if (score >= 90) {
System.out.println("Excellent!");
}
// if-else - executes one of two blocks
if (score >= 60) {
System.out.println("Pass");
} else {
System.out.println("Fail");
}
// if-else-if chain - tests multiple conditions in order
// First condition that evaluates to true executes its block
if (score >= 90) {
System.out.println("A");
} else if (score >= 80) {
System.out.println("B");
} else if (score >= 70) {
System.out.println("C");
} else if (score >= 60) {
System.out.println("D");
} else {
System.out.println("F");
}
// Single statement without braces (not recommended)
// Braces are optional for single statements but prone to errors
if (score >= 60) System.out.println("Pass");
else System.out.println("Fail");
// Dangerous example without braces - misleading indentation
if (score >= 60)
System.out.println("Pass");
System.out.println("Congratulations"); // Always executes! Not part of if
else // Compile error - else has no matching if
System.out.println("Fail");
// Always use braces for clarity
if (score >= 60) {
System.out.println("Pass");
System.out.println("Congratulations"); // Correct - inside if block
} else {
System.out.println("Fail");
}
// Ternary operator - compact conditional expression
// Syntax: condition ? valueIfTrue : valueIfFalse
String result = score >= 60 ? "Pass" : "Fail";
int bonus = score >= 90 ? 100 : 0;
// Ternary can be nested but becomes hard to read
String grade = score >= 90 ? "A" :
score >= 80 ? "B" :
score >= 70 ? "C" :
score >= 60 ? "D" : "F";
// Better approach for complex logic - use if-else
String grade;
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else if (score >= 70) {
grade = "C";
} else if (score >= 60) {
grade = "D";
} else {
grade = "F";
}
// Compound conditions with logical operators
if (score >= 60 && attendance >= 80) {
System.out.println("Pass with good attendance");
}
if (score >= 90 || extraCredit > 0) {
System.out.println("Excellent or has extra credit");
}
if (!(score < 60)) { // Equivalent to score >= 60
System.out.println("Not failing");
}
// Short-circuit evaluation
// && stops at first false, || stops at first true
if (obj != null && obj.getValue() > 0) { // Safe - obj checked first
System.out.println("Valid positive value");
}
// Nested if statements
if (score >= 60) {
if (attendance >= 80) {
System.out.println("Pass with good attendance");
} else {
System.out.println("Pass but attendance warning");
}
} else {
System.out.println("Fail");
}
// Common mistakes
// Assignment instead of comparison (compile error unless boolean)
int x = 5;
if (x = 10) { // Compile error - x is int, not boolean
System.out.println("x is 10");
}
// With boolean variable (compiles but probably wrong)
boolean flag = false;
if (flag = true) { // Sets flag to true and evaluates to true
System.out.println("Always executes!"); // Not what you meant!
}
// Correct comparison
if (x == 10) {
System.out.println("x equals 10");
}
// Dangling else problem - else associates with nearest if
if (score >= 60)
if (attendance >= 80)
System.out.println("Pass with good attendance");
else // This else belongs to inner if, not outer if!
System.out.println("Poor attendance");
// Fix with braces to clarify intent
if (score >= 60) {
if (attendance >= 80) {
System.out.println("Pass with good attendance");
}
} else { // Now clearly belongs to outer if
System.out.println("Fail");
}
Exam Tip: The condition in an if statement must be a boolean expression.
if (x = 5) is a compile error when x is not boolean (assignment, not comparison). if (x == 5) is correct. Be careful with the dangling else problem - the else always associates with the nearest if. Always use braces for clarity.
Switch Statements and Expressions
Switch statements provide an elegant way to handle multiple possible values of a single variable. Java 14+ introduced switch expressions with arrow syntax, and Java 21 added pattern matching capabilities.
// Traditional switch statement (before Java 14)
int day = 3;
String dayName;
switch (day) {
case 1:
dayName = "Monday";
break; // break prevents fall-through
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
default:
dayName = "Invalid";
break;
}
// Fall-through behavior (often unintentional bug)
switch (day) {
case 1:
System.out.println("Monday");
// Missing break - falls through to next case!
case 2:
System.out.println("Tuesday"); // Executes for both 1 and 2
break;
}
// Intentional fall-through (grouping cases)
switch (day) {
case 1:
case 2:
case 3:
case 4:
case 5:
System.out.println("Weekday");
break;
case 6:
case 7:
System.out.println("Weekend");
break;
}
// Switch expression with arrow syntax (Java 14+)
// No fall-through, no break needed, must be exhaustive
String dayName = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
case 4 -> "Thursday";
case 5 -> "Friday";
case 6, 7 -> "Weekend"; // Multiple cases with comma
default -> "Invalid"; // Required if not exhaustive
};
// Switch expression with blocks and yield
// Use yield to return value from block
String dayType = switch (day) {
case 1, 2, 3, 4, 5 -> {
System.out.println("Processing weekday");
yield "Weekday"; // yield returns value from block
}
case 6, 7 -> {
System.out.println("Processing weekend");
yield "Weekend";
}
default -> {
System.out.println("Invalid day");
yield "Invalid";
}
};
// Switch expression must be exhaustive or have default
String result = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
// Compile error! Missing cases and no default
};
// Switch statement vs switch expression
// Statement: void, can have incomplete cases, needs break
switch (day) {
case 1:
System.out.println("Monday");
// No value returned
}
// Expression: returns value, must be exhaustive, no break needed
String name = switch (day) {
case 1 -> "Monday";
default -> "Other";
};
// Mixing colon and arrow syntax is NOT allowed
String result = switch (day) {
case 1: // Colon syntax
yield "Monday";
case 2 -> "Tuesday"; // Compile error - cannot mix syntaxes
};
// Supported types for switch:
// - byte, short, char, int (and their wrapper types)
// - String
// - enum types
// - Pattern types (Java 21+)
// NOT supported: long, float, double, boolean
// Switch on String
String command = "start";
switch (command) {
case "start" -> System.out.println("Starting...");
case "stop" -> System.out.println("Stopping...");
case "pause" -> System.out.println("Pausing...");
default -> System.out.println("Unknown command");
}
// String comparison is case-sensitive
String input = "Start"; // Capital S
switch (input) {
case "start" -> System.out.println("Match"); // Does not match!
default -> System.out.println("No match");
}
// Switch on enum
enum Status { ACTIVE, INACTIVE, PENDING }
Status status = Status.ACTIVE;
String message = switch (status) {
case ACTIVE -> "Currently active";
case INACTIVE -> "Not active";
case PENDING -> "Waiting for activation";
};
// Can throw exceptions from switch
int divide(int a, int b) {
return switch (b) {
case 0 -> throw new ArithmeticException("Division by zero");
default -> a / b;
};
}
Pattern Matching in Switch (Java 21)
Pattern matching allows switches to test not just values but also types and conditions, making code more expressive and type-safe.
// Type pattern matching - switch on type
Object obj = "Hello";
String result = switch (obj) {
case Integer i -> "Integer: " + i;
case String s -> "String: " + s;
case Double d -> "Double: " + d;
case null -> "Null value";
default -> "Unknown type: " + obj.getClass().getName();
};
// Pattern variable is scoped to its case
switch (obj) {
case String s -> {
System.out.println(s.length()); // s is in scope
System.out.println(s.toUpperCase());
}
case Integer i -> {
System.out.println(i * 2); // i is in scope, s is not
}
}
// Guarded patterns with when clause
// Allows additional conditions on pattern
String describe(Object obj) {
return switch (obj) {
case String s when s.isEmpty() -> "Empty string";
case String s when s.length() < 5 -> "Short string: " + s;
case String s when s.length() > 20 -> "Long string";
case String s -> "Normal string: " + s;
case Integer i when i < 0 -> "Negative number: " + i;
case Integer i when i == 0 -> "Zero";
case Integer i -> "Positive number: " + i;
case null -> "Null value";
default -> "Other type";
};
}
// Order matters! More specific patterns must come first
switch (obj) {
case String s -> "String"; // More specific
case Object o -> "Object"; // More general
// Correct order - String is checked first
}
switch (obj) {
case Object o -> "Object"; // Catches everything!
case String s -> "String"; // Unreachable - compile error!
}
// Guarded patterns must be ordered correctly too
switch (obj) {
case String s when s.isEmpty() -> "Empty"; // Most specific
case String s when s.length() < 5 -> "Short"; // Less specific
case String s -> "String"; // Most general for String
}
// Pattern dominance - compile error if pattern is dominated
switch (obj) {
case String s -> "Any string";
case String s when s.isEmpty() -> "Empty"; // Compile error - dominated by above
}
// null handling in pattern matching
// Traditionally, switch throws NullPointerException for null
// With patterns, can explicitly handle null
Object value = null;
switch (value) {
case null -> System.out.println("Null"); // Explicitly handles null
case String s -> System.out.println("String: " + s);
default -> System.out.println("Other");
}
// Without null case, switch throws NullPointerException
switch (value) {
case String s -> System.out.println(s); // NPE if value is null!
default -> System.out.println("Other");
}
// Total patterns - pattern that matches all non-null values of the type
switch (obj) {
case String s -> "String";
case Object o -> "Other object"; // Total pattern for Object
// No default needed - all cases covered
}
// Enum exhaustiveness - no default needed if all values covered
enum Season { SPRING, SUMMER, FALL, WINTER }
String getWeather(Season season) {
return switch (season) {
case SPRING -> "Mild and rainy";
case SUMMER -> "Hot and sunny";
case FALL -> "Cool and breezy";
case WINTER -> "Cold and snowy";
// No default needed - all enum values covered
};
}
// If enum changes, compiler will catch missing case
// This is better than default, which would hide missing cases
// Sealed classes with pattern matching
sealed interface Shape permits Circle, Rectangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
double getArea(Shape shape) {
return switch (shape) {
case Circle(double r) -> Math.PI * r * r; // Deconstruction pattern
case Rectangle(double w, double h) -> w * h; // Deconstruction pattern
// No default needed - all permitted types covered
};
}
// Record patterns - destructure records in switch
record Point(int x, int y) {}
String classifyPoint(Point p) {
return switch (p) {
case Point(0, 0) -> "Origin";
case Point(int x, 0) -> "On X-axis";
case Point(0, int y) -> "On Y-axis";
case Point(int x, int y) when x == y -> "On diagonal";
case Point(int x, int y) -> "General point";
};
}
// Parenthesized patterns
switch (obj) {
case (String s) -> "String"; // Parentheses allowed but optional
case Integer i -> "Integer";
}
Exam Tip: In switch expressions with pattern matching, order matters - more specific patterns must come before general ones. Use
when (not if) for guards. Switch expressions must be exhaustive (cover all cases) or have a default. null can be explicitly handled with case null. For enums, if all values are covered, no default is needed. Mixing colon and arrow syntax in the same switch is not allowed.
Loops
Loops allow repeated execution of code blocks. Java provides four types of loops: for, enhanced for (for-each), while, and do-while.
// Traditional for loop
// Syntax: for (initialization; condition; update)
for (int i = 0; i < 10; i++) {
System.out.println(i); // Prints 0 through 9
}
// All three parts are optional
int i = 0;
for (; i < 10; i++) { // No initialization
System.out.println(i);
}
for (int j = 0; j < 10;) { // No update
System.out.println(j);
j++;
}
for (int k = 0; ; k++) { // No condition - infinite loop
if (k >= 10) break;
System.out.println(k);
}
// Multiple variables in initialization and update
for (int x = 0, y = 10; x < y; x++, y--) {
System.out.println("x=" + x + ", y=" + y);
}
// Variables must be same type
for (int a = 0, b = 0; a < 10; a++, b += 2) { // Both int - OK
System.out.println(a + " " + b);
}
for (int m = 0, double n = 0.0; m < 10; m++, n += 0.5) { // Compile error - different types
}
// Scope of loop variable
for (int n = 0; n < 5; n++) {
System.out.println(n);
}
// System.out.println(n); // Compile error - n not in scope
// Variable declared outside loop - remains in scope
int p = 0;
for (p = 0; p < 5; p++) {
System.out.println(p);
}
System.out.println(p); // OK - p is still in scope, value is 5
// Enhanced for loop (for-each) - iterates over collections and arrays
int[] numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
System.out.println(num); // Prints each element
}
// Works with any Iterable
List names = List.of("Alice", "Bob", "Charlie");
for (String name : names) {
System.out.println(name);
}
// Loop variable is read-only copy
int[] values = {10, 20, 30};
for (int value : values) {
value = value * 2; // Modifies copy, not array element
}
System.out.println(values[0]); // Still 10, not 20
// To modify array elements, use traditional for loop
for (int i = 0; i < values.length; i++) {
values[i] = values[i] * 2; // Modifies actual array element
}
// Cannot use enhanced for with index
// for (int i : numbers) won't give you the index, just the value
// while loop - condition checked before each iteration
int count = 0;
while (count < 5) {
System.out.println(count); // Prints 0 through 4
count++;
}
// May not execute at all if condition is initially false
int x = 10;
while (x < 5) {
System.out.println(x); // Never executes
}
// do-while loop - condition checked after each iteration
// Always executes at least once
int y = 10;
do {
System.out.println(y); // Prints 10 (even though condition is false)
y--;
} while (y > 10);
// Use do-while when you need at least one execution
String input;
do {
input = getUserInput();
} while (!isValid(input));
// Comparing while and do-while
// while: zero or more iterations
// do-while: one or more iterations
// Infinite loops
while (true) {
// Must use break to exit
if (condition) break;
}
for (;;) { // Empty for loop is infinite
// Must use break to exit
if (condition) break;
}
// Nested loops
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
System.out.print("(" + row + "," + col + ") ");
}
System.out.println();
}
// Output:
// (0,0) (0,1) (0,2)
// (1,0) (1,1) (1,2)
// (2,0) (2,1) (2,2)
// Loop with array length
int[] array = {1, 2, 3, 4, 5};
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
// Common loop mistakes
// Off-by-one error
for (int i = 0; i <= array.length; i++) { // WRONG - should be i < array.length
System.out.println(array[i]); // ArrayIndexOutOfBoundsException when i == length
}
// Infinite loop due to wrong condition
for (int i = 0; i > -10; i++) { // Never false if i starts at 0
System.out.println(i); // Infinite loop!
}
// Modifying loop variable in for-each
List list = new ArrayList<>(List.of(1, 2, 3));
for (Integer num : list) {
list.remove(num); // ConcurrentModificationException!
}
// Correct way - use Iterator or traditional for loop
for (int i = list.size() - 1; i >= 0; i--) {
list.remove(i); // Safe - iterating backwards
}
Exam Tip: The enhanced for loop (for-each) cannot be used to modify the collection/array being iterated over. Loop variables in enhanced for loops are effectively final. In traditional for loops, all three parts (initialization, condition, update) are optional, but the semicolons are required. Variables in the initialization must be of the same type. The do-while loop always executes at least once because the condition is checked after the first iteration.
Break, Continue, and Labels
Break and continue statements provide control over loop execution. Labels allow break and continue to target specific loops in nested structures.
// break - immediately exits the current loop
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // Exits loop when i is 5
}
System.out.println(i); // Prints 0, 1, 2, 3, 4
}
System.out.println("Loop ended");
// break in nested loop - only exits innermost loop
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) {
break; // Exits inner loop only
}
System.out.println("i=" + i + ", j=" + j);
}
}
// Output:
// i=0, j=0
// i=1, j=0
// i=2, j=0
// break in switch - exits switch, not enclosing loop
for (int i = 0; i < 5; i++) {
switch (i) {
case 2:
break; // Exits switch, continues loop
default:
System.out.println(i);
}
}
// Prints 0, 1, 3, 4 (skips 2)
// continue - skips rest of current iteration, continues with next
for (int i = 0; i < 5; i++) {
if (i == 2) {
continue; // Skip when i is 2
}
System.out.println(i); // Prints 0, 1, 3, 4
}
// continue with while loop
int count = 0;
while (count < 5) {
count++;
if (count == 3) {
continue; // Skip rest of iteration
}
System.out.println(count); // Prints 1, 2, 4, 5
}
// continue with do-while
int n = 0;
do {
n++;
if (n % 2 == 0) {
continue; // Skip even numbers
}
System.out.println(n); // Prints odd numbers
} while (n < 10);
// Labels - named locations in code
// Label syntax: labelName: followed by statement
// Labeled break - exits the labeled loop
OUTER: for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) {
break OUTER; // Exits outer loop completely
}
System.out.println("i=" + i + ", j=" + j);
}
}
System.out.println("Both loops ended");
// Output:
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0
// Both loops ended
// Without label - only inner loop exits
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) {
break; // Exits inner loop only
}
System.out.println("i=" + i + ", j=" + j);
}
}
// Continues with i=2
// Labeled continue - continues the labeled loop
OUTER: for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) {
continue OUTER; // Skips to next i iteration
}
System.out.println("i=" + i + ", j=" + j);
}
}
// Output:
// i=0, j=0
// i=1, j=0
// i=2, j=0
// Label can only target enclosing statement
OUTER: for (int i = 0; i < 3; i++) {
INNER: for (int j = 0; j < 3; j++) {
if (i == 1) {
break INNER; // OK - INNER encloses this statement
// break OUTER; // Also OK - OUTER encloses this statement
}
}
}
// Cannot break or continue to label that doesn't enclose the statement
LABEL1: for (int i = 0; i < 3; i++) {
System.out.println(i);
}
LABEL2: for (int j = 0; j < 3; j++) {
// break LABEL1; // Compile error - LABEL1 doesn't enclose this
}
// Labels can be on any statement, but break/continue only work with loops
BLOCK: {
System.out.println("Before");
if (condition) {
break BLOCK; // Exits the block
}
System.out.println("After"); // Skipped if condition is true
}
// Cannot use continue on non-loop labels
LABEL: {
// continue LABEL; // Compile error - LABEL not a loop
}
// Practical example - searching 2D array
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int target = 5;
boolean found = false;
SEARCH: for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == target) {
System.out.println("Found at [" + i + "][" + j + "]");
found = true;
break SEARCH; // Exit both loops when found
}
}
}
if (!found) {
System.out.println("Not found");
}
// Without labels - need flag variable
boolean found = false;
for (int i = 0; i < matrix.length && !found; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == target) {
found = true;
break; // Exit inner loop
}
}
}
// Labels with while loops
OUTER: while (condition1) {
while (condition2) {
if (shouldExitBoth) {
break OUTER;
}
if (shouldSkipInner) {
continue; // Continues inner loop
}
if (shouldSkipOuter) {
continue OUTER; // Continues outer loop
}
}
}
// Label naming conventions
// Labels are usually UPPERCASE by convention
// but this is not required
outer: for (int i = 0; i < 3; i++) { // lowercase - valid but not conventional
inner: for (int j = 0; j < 3; j++) {
break outer;
}
}
Exam Tip: Labels can only be used with break and continue statements, and only to target an enclosing loop or block. You cannot use labels to jump to arbitrary code locations (Java has no goto). Labels are case-sensitive. break exits the loop completely; continue skips to the next iteration. In switch statements, break exits the switch, not an enclosing loop. Labeled break is useful for exiting nested loops efficiently.
Assertions
Assertions are used to verify assumptions during development and testing. They help catch programming errors early but should not be used for normal error handling.
// Basic assertion syntax
// assert condition;
// If condition is false, throws AssertionError
int age = 25;
assert age >= 0; // Passes silently if true
// Assertion with custom message
// assert condition : message;
// Message is included in AssertionError if assertion fails
assert age >= 0 : "Age cannot be negative";
// Expression after colon can be any type - converted to string
assert age >= 0 : "Age is " + age;
assert age >= 0 : age; // Integer converted to string
// Enabling and disabling assertions at runtime
// Enable all assertions:
// java -ea MyClass
// java -enableassertions MyClass
// Enable assertions for specific package:
// java -ea:com.mycompany... MyClass
// java -ea:com.mycompany.mypackage MyClass
// Enable assertions for specific class:
// java -ea:com.mycompany.MyClass MyClass
// Disable all assertions (default):
// java -da MyClass
// java -disableassertions MyClass
// Disable specific package:
// java -da:com.mycompany... MyClass
// Combine enable and disable:
// java -ea -da:com.thirdparty... MyClass
// System assertions (Java runtime):
// java -esa MyClass (enable system assertions)
// java -dsa MyClass (disable system assertions)
// What happens when assertion fails
int value = -5;
assert value >= 0 : "Value is " + value;
// Throws: java.lang.AssertionError: Value is -5
// AssertionError is an Error, not an Exception
try {
assert false : "Failed";
} catch (AssertionError e) {
System.out.println("Caught: " + e.getMessage());
}
// When to use assertions
// 1. Internal invariants - conditions that must be true at certain points
private void processData(int[] data) {
// Internal invariant - data should never be null here
assert data != null : "Data array is null";
// Process data
for (int i = 0; i < data.length; i++) {
// Internal invariant - all values should be positive
assert data[i] > 0 : "Negative value at index " + i;
}
}
// 2. Control flow invariants - code that should be unreachable
switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY:
return "Weekday";
case SATURDAY, SUNDAY:
return "Weekend";
default:
assert false : "Unknown day: " + day;
return null; // Needed for compiler, but should never execute
}
// 3. Preconditions in private methods (not public methods!)
private int computeArea(int width, int height) {
assert width > 0 : "Width must be positive";
assert height > 0 : "Height must be positive";
return width * height;
}
// 4. Postconditions - conditions that must be true after method completes
private List processItems(List items) {
// Processing logic
List result = new ArrayList<>();
for (String item : items) {
result.add(item.toUpperCase());
}
// Postcondition - result should have same size as input
assert result.size() == items.size() : "Size mismatch";
return result;
}
// 5. Class invariants - conditions that should always be true for object
class BankAccount {
private double balance;
public void deposit(double amount) {
balance += amount;
assert balance >= 0 : "Balance became negative"; // Class invariant
}
public void withdraw(double amount) {
balance -= amount;
assert balance >= 0 : "Balance became negative"; // Class invariant
}
}
// When NOT to use assertions
// 1. NOT for argument checking in public methods
// Public API should always validate arguments regardless of assertions
public void setAge(int age) {
// WRONG - assertions can be disabled
assert age >= 0 : "Age cannot be negative";
// CORRECT - always validate public method arguments
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
this.age = age;
}
// 2. NOT for side effects - assertions can be disabled!
// BAD - side effect in assertion
List items = new ArrayList<>();
assert items.add("test"); // add() returns true, but has side effect!
// If assertions disabled, item never added!
// GOOD - separate side effect from assertion
boolean added = items.add("test");
assert added : "Failed to add item";
// 3. NOT for checking conditions that can occur in normal operation
// BAD - user input validation should not use assertions
String input = readUserInput();
assert !input.isEmpty() : "Input is empty"; // WRONG - can happen normally
// GOOD - normal validation
if (input.isEmpty()) {
throw new IllegalArgumentException("Input cannot be empty");
}
// 4. NOT for command line argument validation
public static void main(String[] args) {
// WRONG - assertions can be disabled
assert args.length > 0 : "No arguments provided";
// CORRECT - always validate
if (args.length == 0) {
System.err.println("Usage: java MyClass ");
System.exit(1);
}
}
// Checking if assertions are enabled
boolean assertionsEnabled = false;
assert assertionsEnabled = true; // Side effect only if assertions enabled
if (assertionsEnabled) {
System.out.println("Assertions are enabled");
} else {
System.out.println("Assertions are disabled");
}
// Assertions with complex conditions
assert items.stream().allMatch(item -> item != null) : "List contains null";
assert result >= min && result <= max : "Result out of range: " + result;
// Assertions in loops
for (int i = 0; i < array.length; i++) {
assert array[i] != null : "Null element at index " + i;
process(array[i]);
}
// Assertions with message computation
// Message expression only evaluated if assertion fails
assert isValid(data) : computeExpensiveErrorMessage(data);
// computeExpensiveErrorMessage() only called if isValid() returns false
// Best practices
// 1. Use assertions to document assumptions
// 2. Do not catch AssertionError (defeats the purpose)
// 3. Keep assertion checks simple and fast
// 4. Use descriptive messages
// 5. Test both with and without assertions enabled
Exam Tip: Assertions are disabled by default - use
-ea or -enableassertions to enable them. When an assertion fails, it throws AssertionError (an Error, not Exception). Never use assertions for argument validation in public methods, as they can be disabled. Do not put side effects in assertion conditions - if assertions are disabled, the side effects won't occur. Assertions are for catching programming bugs during development, not for runtime error handling.
1Z0-830 Java SE 21 Certification - Table of Contents
Master all exam topics with comprehensive study guides and practice examples.
Popular Posts
1Z0-830 Java SE 21 Developer Certification
Azure AI Foundry Hello World
Azure AI Agent Hello World
Foundry vs Hub Projects
Build Agents with SDK
Bing Web Search Agent
Function Calling Agent
Spring Boot + Azure Key Vault Hello World Example
Spring Boot + Elasticsearch + Azure Key Vault Example
Spring Boot Azure AD (Entra ID) OAuth 2.0 Authentication
Deploy Spring Boot App to Azure App Service
Secure Azure App Service using Azure API Management
Deploy Spring Boot JAR to Azure App Service
Deploy Spring Boot + MySQL to Azure App Service
Spring Boot + Azure Managed Identity Example
Secure Spring Boot Azure Web App with Managed Identity + App Registration
Elasticsearch 8 Security - Integrate Azure AD OIDC