Introduction: Why Error Handling in Java is an Art
\nError handling in Java is not just a mechanism for preventing program crashes. It is a tool for designing reliable, readable, and predictable architecture. Every developer is familiar with the try/catch construct, but at an advanced level, it is important to understand the nuances: when to use checked exceptions, how to properly close resources, and why multi-catch can save you from code duplication. In this article, we will explore advanced techniques for working with exceptions in Java that will help you write cleaner and safer code.
1. Multi-catch and Combining Exceptions
\nBefore Java 7, handling different types of exceptions in a single catch block was impossible. You had to either duplicate code or use a common parent class (e.g., Exception), which reduced the informativeness of the error. With the advent of multi-catch, this changed.
Multi-catch Syntax
\nYou can list multiple exception types separated by a vertical bar (|) in a single catch block. This is convenient when the handling logic for different exceptions is identical.
try {\n // Code that may throw FileNotFoundException or IOException\n FileReader reader = new FileReader("file.txt");\n int data = reader.read();\n} catch (FileNotFoundException | IOException e) {\n System.err.println("I/O error: " + e.getMessage());\n}\nImportant: the variable e in such a block is final (or effectively final). This means it cannot be reassigned, but methods of the common superclass (e.g., getMessage()) can be called.
When to use multi-catch?
\n- \n
- If the exceptions are not related by hierarchy (e.g.,
IOExceptionandSQLException). \n - If the handling logic is completely identical. \n
- If you want to avoid duplicating
catchblocks. \n
2. Try-with-resources: Automatic Resource Management
\nOne of the most common mistakes in Java is forgetting to close a resource (file, socket, database connection). Manual closing in a finally block leads to verbose code and the risk of leaks. The try-with-resources construct (Java 7+) solves this problem.
How it works
\nThe resource must implement the AutoCloseable (or Closeable) interface. After the try block completes (or an exception occurs), the resource will be automatically closed.
try (FileReader reader = new FileReader("file.txt");\n BufferedReader br = new BufferedReader(reader)) {\n String line = br.readLine();\n System.out.println(line);\n} catch (IOException e) {\n System.err.println("Error: " + e.getMessage());\n}\n// reader and br are closed automatically\nAdvanced Features
\n- \n
- Multiple resources: they can be declared separated by semicolons. Closing occurs in reverse order. \n
- Suppressed exceptions: if an exception is thrown when closing a resource, but a main exception has already been thrown, Java "suppresses" the closing exception. It can be retrieved via the
getSuppressed()method. \n - Using with custom resources: any class implementing
AutoCloseablecan be used intry-with-resources. \n
3. Creating Custom Exceptions and Best Practices
\nBuilt-in Java exceptions are often insufficient for business logic. Creating custom exception classes is a standard practice. However, it is important to do it correctly.
\nChecked vs. Unchecked Exceptions
\nFollow a simple rule: if the exception can be handled by the client (e.g., invalid input data format), make it a checked (inher