Introduction: Why Error Handling is Critical for a Java Developer
Any program that goes beyond "Hello, World!" encounters unexpected situations: the user enters letters instead of numbers, a file is not found, the network is unavailable. In Java, a powerful and elegant mechanism of exceptions has been developed for these cases. Competent error handling is not just "turning a blind eye to the problem," but creating resilient, predictable, and secure code.
In this article, we will analyze not only the classic try/catch construct, but also advanced techniques: try-with-resources, creating custom exceptions, and best practices that will help you write professional Java code.
1. Basics: Exception Hierarchy and the try/catch/finally Construct
Before catching errors, you need to understand their nature. In Java, all exceptions are descendants of the Throwable class. The main branches are Exception (exceptions that can and should be handled) and Error (critical JVM errors that are pointless to handle).
The key division within Exception is into checked and unchecked exceptions.
- Checked exceptions (e.g.,
IOException,SQLException) — the compiler forces you to handle them. These are predictable external failures. - Unchecked exceptions (descendants of
RuntimeException:NullPointerException,ArrayIndexOutOfBoundsException) — logic errors in the program. The compiler does not require handling them, but that doesn't mean they can be ignored.
Basic try-catch-finally Construct
The most common way to handle errors is the try block, which contains the "dangerous" code, and one or more catch blocks for catching specific types of exceptions.
import java.io.*;
public class ReadFileExample { public static void main(String[] args) { BufferedReader reader = null; try { reader = new BufferedReader(new FileReader("data.txt")); String line = reader.readLine(); System.out.println("Read: " + line); } catch (FileNotFoundException e) { System.err.println("Error: File not found! " + e.getMessage()); } catch (IOException e) { System.err.println("Input/Output error: " + e.getMessage()); } finally { // finally ALWAYS executes, even if there was an exception or return try { if (reader != null) { reader.close(); // Important to close the resource } } catch (IOException e) { System.err.println("Error closing file: " + e.getMessage()); } } }}Important: The finally block is needed for guaranteed resource cleanup (closing files, database connections). However, starting from Java 7, a more elegant way appeared.
2. Modern Approach: try-with-resources (Java 7+)
Forgetting to close a file or connection is one of the most common mistakes for beginners. The try-with-resources construct automatically closes all resources that implement the AutoCloseable (or Closeable) interface. The code becomes shorter, cleaner, and safer.
import java.io.*;
public class TryWithResourcesExample { public static void main(String[] args) { // Resources are declared in parentheses after try try (BufferedReader reader = new BufferedReader(new FileReader("data.txt")); BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
String line; while ((line = reader.readLine()) != null) {