Java Exceptions
Java Exception Handling
Section titled “Java Exception Handling”An exception is an event that occurs during program execution that disrupts the normal flow of instructions. Java’s exception handling mechanism provides a robust way to manage these events.
Common causes of exceptions:
- Invalid user input (e.g., entering text where a number is expected).
- Resource issues (e.g., file not found, network connection lost, database unavailable).
- Programming errors (e.g., accessing an array element out of bounds, trying to use a
nullreference). - Resource exhaustion (e.g., running out of memory).
Categories of Exceptions/Errors
Section titled “Categories of Exceptions/Errors”Java classifies throwable objects (subclasses of java.lang.Throwable) into three main categories:
- Checked Exceptions: Subclasses of
Exception(excludingRuntimeExceptionand its subclasses). These represent exceptional conditions that a well-written application should anticipate and recover from (e.g.,IOException,FileNotFoundException,SQLException). The Java compiler requires that you handle checked exceptions, either by catching them using atry-catchblock or by declaring them in the method signature using thethrowskeyword (the “Handle or Declare Rule”). - Unchecked Exceptions (Runtime Exceptions): Subclasses of
RuntimeException. These typically indicate programming errors (e.g.,NullPointerException,ArrayIndexOutOfBoundsException,IllegalArgumentException,ClassCastException). The compiler does not require you to handle or declare unchecked exceptions, although you can catch them if needed. - Errors: Subclasses of
Error. These represent serious problems that are usually beyond the control of the application and from which recovery is generally not possible (e.g.,OutOfMemoryError,StackOverflowError,NoClassDefFoundError). Applications typically do not try to catchErrors.
Exception Hierarchy
Section titled “Exception Hierarchy”All exception and error types inherit from the java.lang.Throwable class.
`Throwable` ├── `Error` (Unrecoverable problems, usually not caught) │ ├── `VirtualMachineError` (e.g., `OutOfMemoryError`, `StackOverflowError`) │ └── ... (other critical errors) └── `Exception` (Conditions that applications might want to catch) ├── `IOException` (Checked - Related to I/O failures) │ └── `FileNotFoundException` (Checked) ├── `SQLException` (Checked - Related to database access) ├── `RuntimeException` (Unchecked - Programming errors or unexpected conditions) │ ├── `NullPointerException` │ ├── `ArrayIndexOutOfBoundsException` │ ├── `IllegalArgumentException` │ ├── `ClassCastException` │ └── ... (other runtime exceptions) └── ... (other checked exceptions like `ClassNotFoundException`)Key Throwable Methods
Section titled “Key Throwable Methods”Useful methods available on any exception or error object:
| Method | Description |
|---|---|
String getMessage() | Returns the detail message string describing the exception. |
String toString() | Returns a short description of the throwable (class name + result of getMessage()). |
void printStackTrace() | Prints the throwable’s stack trace (the sequence of method calls leading to the exception) to the standard error stream. Very useful for debugging. |
void printStackTrace(PrintStream s) / void printStackTrace(PrintWriter s) | Prints the stack trace to the specified stream/writer. |
StackTraceElement[] getStackTrace() | Returns an array of stack trace elements, allowing programmatic access to the stack information. |
Throwable getCause() | Returns the underlying cause of this throwable (if one was specified, used for exception chaining), or null. |
Handling Exceptions: try, catch, finally
Section titled “Handling Exceptions: try, catch, finally”Java provides keywords to handle exceptions:
try: Encloses the block of code (protected code) that might throw an exception.catch: Follows atryblock. Catches and handles a specific type of exception. Atryblock can have multiplecatchblocks to handle different exception types.finally: Follows atryblock and anycatchblocks. The code insidefinallyalways executes, regardless of whether an exception was thrown or caught in thetryblock. It’s typically used for cleanup operations (like closing files or network connections), thoughtry-with-resourcesis now often preferred for resource management.
Basic Syntax:
try { // Code that might throw an exception(s) // ...} catch (SpecificExceptionType1 e1) { // Handle SpecificExceptionType1 System.err.println("Caught exception 1: " + e1.getMessage()); // e1.printStackTrace(); // Often useful for debugging} catch (SpecificExceptionType2 e2) { // Handle SpecificExceptionType2} catch (Exception e) { // Catch broader exceptions (like Exception) last // Handle any other Exception subclass not caught above} finally { // Cleanup code that always executes // e.g., close resources (if not using try-with-resources) System.out.println("Finally block executed.");}// Code here executes after try-catch-finally completes (unless program exits)Example: Catching ArrayIndexOutOfBoundsException
Section titled “Example: Catching ArrayIndexOutOfBoundsException”This code attempts to access an invalid array index, causing an exception.
// File: ExceptionDemo.javapublic class ExceptionDemo { public static void main(String[] args) { try { int[] numbers = new int[2]; // Array has indices 0 and 1 System.out.println("Accessing element at index 3..."); numbers[3] = 10; // This line will throw an exception System.out.println("This line will not be executed."); } catch (ArrayIndexOutOfBoundsException e) { // Catch the specific exception System.err.println("Error: Array index is out of bounds!"); System.err.println("Exception details: " + e); // Uses toString() // e.printStackTrace(); // Uncomment for full stack trace } finally { System.out.println("The 'finally' block is always executed."); } System.out.println("Program continues after the try-catch-finally block."); }}Output:
Accessing element at index 3...Error: Array index is out of bounds!Exception details: java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 2The 'finally' block is always executed.Program continues after the try-catch-finally block.Multiple catch Blocks and Multi-Catch
Section titled “Multiple catch Blocks and Multi-Catch”You can have multiple catch blocks to handle different types of exceptions. The first catch block whose parameter type matches (or is a superclass of) the thrown exception type is executed. Order matters: catch more specific exceptions before more general ones.
Java 7 introduced multi-catch, allowing you to catch multiple exception types in a single block if they share the same handling logic:
try { // Code that might throw IOException or SQLException} catch (IOException | SQLException ex) { // Handle both IOException and SQLException here logger.error("I/O or SQL error: ", ex); // Note: 'ex' is implicitly final in a multi-catch block}The try-with-resources Statement
Section titled “The try-with-resources Statement”Java 7 introduced the try-with-resources statement, which automatically closes resources (objects that implement java.lang.AutoCloseable or java.io.Closeable, like streams, sockets, database connections) when the try block finishes, whether normally or due to an exception. This is the preferred way to manage resources, as it makes code cleaner and less error-prone than using finally blocks for closing.
Syntax:
// Resource(s) declared in parentheses after trytry (FileInputStream fis = new FileInputStream("file.txt"); BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
// Use the resources (fis, reader) String line = reader.readLine(); // ... process file ...
} catch (IOException e) { // Handle potential exceptions during resource usage or closing System.err.println("Error processing file: " + e.getMessage());}// 'reader' and 'fis' are automatically closed here, even if exceptions occur.Declaring Exceptions: throws Keyword
Section titled “Declaring Exceptions: throws Keyword”If a method might throw a checked exception but does not handle it with try-catch, it must declare that it might throw the exception using the throws keyword in its signature. This informs callers that they need to handle or declare the exception themselves.
import java.io.IOException;import java.nio.file.Files;import java.nio.file.Paths;import java.util.List;
public class FileReaderUtil {
// This method declares that it can throw IOException public List<String> readFile(String filePath) throws IOException { // Files.readAllLines() can throw IOException (a checked exception) return Files.readAllLines(Paths.get(filePath)); }
public void processFile() { try { List<String> lines = readFile("data.txt"); // Caller must handle IOException // ... process lines ... } catch (IOException e) { System.err.println("Failed to read file: " + e.getMessage()); } }}A method can declare multiple exceptions separated by commas (e.g., throws IOException, SQLException). You generally don’t declare RuntimeExceptions or Errors using throws.
Throwing Exceptions: throw Keyword
Section titled “Throwing Exceptions: throw Keyword”You can explicitly throw an exception object using the throw keyword. This is often done to signal an error condition discovered within your code.
public void setAge(int age) { if (age < 0) { // Throw an unchecked exception for invalid argument throw new IllegalArgumentException("Age cannot be negative: " + age); } this.age = age;}You can throw newly created exception objects or re-throw an exception caught in a catch block.
Custom Exceptions
Section titled “Custom Exceptions”You can create your own custom exception classes by extending Exception (for checked exceptions) or RuntimeException (for unchecked exceptions). This allows you to create specific exception types relevant to your application’s domain.
Guidelines for Custom Exceptions:
- Extend
Exceptionfor conditions you expect callers to reasonably recover from (checked). - Extend
RuntimeExceptionfor programming errors or unexpected conditions (unchecked). - Follow standard naming conventions (e.g., end class names with
Exception). - Provide constructors that match the superclass constructors (e.g., a no-arg constructor, one taking a message String, one taking a message and a cause Throwable).
- Include a
serialVersionUIDif the custom exception needs to be serializable.
Example: Custom Checked Exception
// File: InsufficientFundsException.javapublic class InsufficientFundsException extends Exception { // Checked exception private static final long serialVersionUID = 1L; private final double shortfall;
public InsufficientFundsException(String message, double shortfall) { super(message); // Pass message to Exception constructor this.shortfall = shortfall; }
public double getShortfall() { return shortfall; }}
// File: BankAccount.javapublic class BankAccount { private double balance;
// ... constructor, deposit ...
public void withdraw(double amount) throws InsufficientFundsException { if (amount < 0) { throw new IllegalArgumentException("Withdrawal amount cannot be negative."); } if (balance >= amount) { balance -= amount; } else { double needs = amount - balance; throw new InsufficientFundsException("Insufficient funds for withdrawal", needs); } } // ... getBalance ...}
// Usage:try { myAccount.withdraw(1000.00);} catch (InsufficientFundsException e) { System.err.println(e.getMessage() + ". Short by: " + e.getShortfall());} catch (IllegalArgumentException iae) { System.err.println("Invalid withdrawal amount: " + iae.getMessage());}Best Practices
Section titled “Best Practices”- Use checked exceptions for recoverable conditions and unchecked exceptions for programming errors.
- Don’t catch
Throwable,Error, or overly broad exceptions likeExceptionunless absolutely necessary and you know how to handle them. - Use
try-with-resourcesfor managing resources. - Provide meaningful messages in exceptions.
- Use exception chaining (
new CustomException("message", cause)) to preserve the original exception context when wrapping exceptions. - Don’t ignore exceptions (empty catch blocks are usually bad practice). Log them or handle them appropriately.
- Clean up resources in
finallyblocks only iftry-with-resourcescannot be used. - Consider using
Optional<T>(Java 8+) as an alternative to returningnullor throwingNullPointerExceptionin some scenarios.