Exception Handling — try/catch/finally, Checked vs Unchecked, and Try-With-Resources
In this tutorial, you will learn about Exception Handling. We cover key concepts, practical examples, and best practices to help you master this topic.
Java exception handling separates error-handling code from normal execution flow using try, catch, finally, and throw keywords. Exceptions are objects that represent abnormal conditions — the JVM or application code throws them when an operation cannot complete normally, and callers catch them to handle the failure gracefully.
What You'll Learn
- The exception hierarchy: Throwable, Exception, RuntimeException, Error
- Checked vs unchecked exceptions
- try/catch/finally and try-with-resources
- Best practices for exception handling
Why It Matters
Proper exception handling distinguishes robust software from fragile software. Unhandled exceptions crash programs or leak sensitive data. Understanding the checked/unchecked distinction helps you design APIs that are both safe and convenient.
Real-World Use
Every I/O operation throws checked exceptions (IOException). Every web framework wraps application exceptions in HTTP error responses. Spring's @ExceptionHandler methods catch exceptions thrown by controllers.
The Exception Hierarchy
Object
└── Throwable
├── Error (unchecked — should not be caught)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── ...
└── Exception (checked, except RuntimeException)
├── RuntimeException (unchecked)
│ ├── NullPointerException
│ ├── IllegalArgumentException
│ ├── IndexOutOfBoundsException
│ └── ...
└── IOException, SQLException, ...
Error— serious problems that a program should not try to catch (OutOfMemoryError, StackOverflowError)Exception— conditions that a program might want to catchRuntimeException(unchecked) — programming bugs (null pointer, illegal argument, index out of bounds)
Checked vs Unchecked
Checked Exceptions
Must be handled or declared:
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path);
// ...
}
The compiler enforces that every checked exception is either caught or declared in the method's throws clause.
Unchecked Exceptions
Do not need to be declared or caught:
public void setName(String name) {
if (name == null) {
throw new IllegalArgumentException("Name cannot be null");
}
this.name = name;
}
The compiler does not check for RuntimeException subclasses.
When to Use Which
- Use checked exceptions for recoverable conditions where the caller must decide what to do (file not found, network timeout)
- Use unchecked exceptions for programming errors (null argument, out-of-bounds index) and conditions the caller cannot reasonably recover from
Try/Catch/Finally
try {
FileReader reader = new FileReader("file.txt");
int data = reader.read();
// ...
} catch (FileNotFoundException e) {
System.err.println("File not found: " + e.getMessage());
} catch (IOException e) {
System.err.println("IO error: " + e.getMessage());
} finally {
System.out.println("This always executes");
}
try— wraps code that may throw exceptionscatch— handles specific exception types (can have multiple)finally— always executes, used for cleanup (closing resources)
Multi-Catch (Java 7+)
try {
// code that may throw multiple exceptions
} catch (IOException | SQLException e) {
System.err.println("Error: " + e.getMessage());
}
Try-With-Resources (Java 7+)
Automatically closes resources implementing AutoCloseable:
try (FileReader reader = new FileReader("file.txt");
BufferedReader br = new BufferedReader(reader)) {
String line = br.readLine();
System.out.println(line);
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
Resources are closed in reverse order of declaration, even if an exception occurs. This is the idiomatic way to handle I/O in modern Java.
Custom AutoCloseable
public class DatabaseConnection implements AutoCloseable {
@Override
public void close() {
System.out.println("Closing connection");
}
}
try (DatabaseConnection conn = new DatabaseConnection()) {
// use connection
} // close() called automatically
Creating and Throwing Exceptions
public void withdraw(double amount) {
if (amount < 0) {
throw new IllegalArgumentException("Amount cannot be negative");
}
if (amount > balance) {
throw new InsufficientFundsException("Balance: " + balance + ", requested: " + amount);
}
balance -= amount;
}
Best Practices
- Catch specific exceptions, not
ExceptionorThrowable. CatchingExceptionhides unexpected errors likeNullPointerException. - Do not swallow exceptions. An empty
catchblock hides failures. At minimum, log the exception. - Use try-with-resources for I/O. It guarantees cleanup and is less error-prone than
finally. - Preserve the cause. When wrapping an exception, pass the original as the cause:
throw new ServiceException("Failed to process", e);
- Fail Fast. Validate inputs early and throw
IllegalArgumentExceptionrather than letting downstream code fail with confusing errors.
Common Mistakes
- Catching
Exceptionin a catch-all block. Hides bugs. Methods likeInterruptedExceptionshould never be silenced. - Forgetting to close resources in
finally. Leads to resource leaks. Use try-with-resources instead. - Throwing
Exceptionin method signatures. Forces callers to catch or declare a generic exception, defeating the purpose of checked exceptions. - Logging and rethrowing. Logging the exception and then throwing a new one creates duplicate log entries. Log at the appropriate layer.
- Using exceptions for control flow. Exceptions are expensive. Do not throw exceptions for expected conditions like "end of file".
Practice Questions
1. What is the difference between checked and unchecked exceptions?
Checked exceptions must be caught or declared (compiler-enforced). Unchecked exceptions (RuntimeException and subclasses) do not require handling.
2. What is the purpose of the finally block?
It always executes, regardless of whether an exception is thrown or caught. Used for cleanup: closing files, releasing locks.
3. How does try-with-resources differ from a regular try/catch/finally?
Try-with-resources automatically closes AutoCloseable resources in reverse order. It reduces boilerplate and prevents resource leaks.
4. What is exception chaining?
Wrapping one exception inside another while preserving the original as the cause. Done via new Exception("message", cause).
5. Why should you avoid catching Exception or Throwable?
You might catch RuntimeException subtypes (like NullPointerException) that indicate bugs, or Error subtypes that signal JVM failure — both should propagate.
Challenge Question:
Write a FileProcessor class that reads numbers from a file (one per line), sums them, and writes the result to another file. Handle FileNotFoundException, IOException, NumberFormatException, and ArithmeticException. Use try-with-resources. Ensure no resource leaks occur even if an exception is thrown mid-processing.
FAQ
Mini Project
Write a program ExceptionDemo.java that:
- Creates a
BankAccountclass withwithdraw(amount)that throwsInsufficientFundsException(custom checked exception) - Creates a
FileAccountStorethat reads/writes accounts from a file using try-with-resources - Demonstrates multi-catch: catch
FileNotFoundExceptionandIOExceptionin one handler - Demonstrates exception chaining: wrap
IOExceptionin aServiceExceptionwith the original cause - Shows
try-with-resourceswith two resources and verifies they close in reverse order - Uses
Objects.requireNonNull()to demonstrate fast-fail validation
What's Next
Java provides many built-in exception classes, but you often need application-specific exceptions. Lesson 22 explores custom exceptions — creating meaningful exception types, chained exceptions, and best practices for exception design.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro