Java exception control flow separates the code that can fail, matching recovery code, and cleanup. try contains the protected operation, catch handles a matching thrown type, and finally normally runs when control leaves that try construct. throw raises an exception object, while throws declares a method's possible failures. These names are similar, but their roles are distinct. This lesson follows concrete execution paths so you can predict what runs after success, a caught failure, or a return.
A try block has a defined failure boundary
Statements in try execute in order until completion or an exception. If a statement throws, later statements in that same block are skipped. Java looks for the first compatible catch clause. A catch only covers the associated try, not an unrelated earlier operation. Keep the protected region focused so handling corresponds to the operation you understand. A very large try block can make the same exception type ambiguous across several different activities.
Catch matching types in a useful order
A catch parameter can name the thrown type or a suitable superclass. Separate subclass catches must precede a broader superclass catch, otherwise they are unreachable. Recovery can display a correction message, retry safely, or translate the exception, depending on the contract. Printing a stack trace and continuing with invalid state is not automatically recovery. Decide which values remain valid before executing code after the catch.
Use finally for cleanup without changing the result
finally normally executes after try and any selected catch, including when the try returns. It is appropriate for releasing a manually managed resource or restoring temporary state. Avoid returning from finally: it can replace a result or suppress a failure that the try was about to propagate. Cleanup can also throw and obscure the original exception. Prefer try-with-resources for AutoCloseable resources because it preserves primary and suppressed failures systematically.
Throw rejects a condition at runtime
throw is followed by an exception object and transfers control immediately. Validate a meaningful precondition before performing work that depends on it. IllegalArgumentException suits an invalid method argument; IllegalStateException suits an operation unavailable in the current object state. Choose a message explaining the violated rule rather than merely repeating the exception name. Do not use exceptions as routine loop control when an ordinary condition describes the expected path.
Throws describes a method contract
A throws clause lists exception types the method may propagate. Checked exceptions create obligations for callers, while unchecked types can be documented there without being mandatory. Declaring a failure does not catch it or guarantee it will occur. The language-setting example defines a checked exception for a required missing configuration value. Its caller must decide whether to obtain another setting or stop that operation. Choose exception types according to the real domain boundary rather than using an I/O exception for unrelated arithmetic.
Know the limits of cleanup promises
finally is part of ordinary Java control flow, not a guarantee against every process-ending event. Abrupt JVM termination or external process failure can prevent normal cleanup. Even when finally runs, a failing cleanup operation needs its own policy. For files and other closeable resources, continue with try-with-resources. Keep logging and result reporting outside the protected calculation when that makes the flow easier to understand.
Practice tasks and checks
Add a valid numeric port string to a second run of CatchFlow and predict which messages differ. Keep the final check message distinct from success. In FinallyReturn, temporarily throw a cleanup exception and observe that main no longer receives twelve; then restore normal cleanup. Explain why this is a reason to keep cleanup simple and use supported resource constructs. For DeclaredSettingFailure, supply a nonblank language and test the normal result, then inspect the catch-or-declare obligation at the call site. Finally, sketch two catches where a specific subtype precedes its superclass and explain why reversing them is rejected. Trace each path before introducing additional nested try blocks.
Java Summary
- A thrown failure skips later statements in its try block.
- finally normally runs before a pending return completes.
- throw performs failure; throws declares the method contract.
- Continue with automatic resource cleanup when these examples are clear.
Java Examples
Each example shows the code, the output it produces and what each part does.
Recover from a specific parse failure
Save this program as CatchFlow.java. Compile it with javac CatchFlow.java and run it with java CatchFlow.
Expected output:
Parsing fails before port receives a value, so the Port print inside try is skipped. The NumberFormatException matches the catch and prints the correction message. Execution then continues after the try-catch construct. The final message does not claim that a valid port was obtained; it says only that this check finished. A real application should stop or request another value before using a required port.
public class CatchFlow {
public static void main(String[] args) {
try {
int port = Integer.parseInt("port");
System.out.println("Port: " + port);
} catch (NumberFormatException error) {
System.out.println("Port must be numeric");
}
System.out.println("Input check finished");
}
}Port must be numeric
Input check finishedObserve finally before a pending return
Save this program as FinallyReturn.java. Compile it with javac FinallyReturn.java and run it with java FinallyReturn.
Expected output:
The return expression evaluates to twelve, but the method must execute finally before completing that return. Cleanup step therefore prints first. Once finally completes normally, main receives twelve and prints it. The example deliberately has no return in finally, preserving the original result. In a real resource operation, replace the diagnostic message with the required cleanup or use a closeable-resource construct.
public class FinallyReturn {
static int calculate() {
try {
return 8 + 4;
} finally {
System.out.println("Cleanup step");
}
}
public static void main(String[] args) {
System.out.println(calculate());
}
}Cleanup step
12Propagate a declared configuration failure
Save this program as DeclaredSettingFailure.java. Compile it with javac DeclaredSettingFailure.java and run it with java DeclaredSettingFailure.
Expected output:
The domain method declares a checked failure because its caller must address a missing required setting. The guard raises that failure for the empty string, so no language is returned. main catches the declared type and displays the requirement. Removing throws without adding handling inside language would fail compilation. The method assumes a non-null setting string; a general configuration loader should decide how null relates to the same missing-value policy.
public class DeclaredSettingFailure {
static class MissingSettingException extends Exception {
MissingSettingException(String message) { super(message); }
}
static String language(String setting) throws MissingSettingException {
if (setting.isBlank()) throw new MissingSettingException("Language setting required");
return setting;
}
public static void main(String[] args) {
try {
System.out.println(language(""));
} catch (MissingSettingException error) {
System.out.println(error.getMessage());
}
}
}Language setting requiredJava FAQ
Does finally run before a return completes?
Under normal control flow, yes. The return expression is evaluated, finally executes, and the pending return completes if cleanup finishes normally. A return or failure inside finally can replace that pending outcome.
Does catching an exception mean the application can continue normally?
No. The catch must establish a valid recovery state or terminate the failed operation meaningfully. A missing required value is still missing after its error is printed.
Can a try block have no catch?
Yes. try followed only by finally runs clean-up code and lets the exception continue to the caller. A try-with-resources statement can also stand alone, because closing the resources is its whole purpose. What isn't allowed is a try with neither catch, finally nor resources.
What happens if catch or finally throws an exception?
The new exception travels up to the caller like any other. If finally throws while an earlier exception is already propagating, the earlier one is lost and only the new one is seen. That's one reason to keep finally blocks short and to prefer try-with-resources, which records clean-up failures as suppressed exceptions.
How do I rethrow an exception after logging it?
Write throw e; at the end of the catch block, or wrap it with context: throw new ReportException("Could not build report", e);. Avoid logging the same exception at every level as it travels up, which floods the log with duplicate stack traces. Log once, where it's handled.
Java Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Which statement is skipped after the parser throws in CatchFlow?
Why does Cleanup step print before twelve?
Java Quiz
2 questions to check what you learned.
What does throw do?
- Raises a specific exception object now
- Lists possible failures only
- Automatically retries the operation
Why avoid returning from finally?
- It can replace a result or hide a failure
- Java never permits it
- It always doubles the result