Skip to content

Exception Handling

Exception handling is a powerful mechanism in Java that allows you to manage runtime errors gracefully, preventing your program from crashing and allowing you to handle errors in a controlled manner.

All exception types are subclasses of the java.lang.Throwable class.

java.lang.Object
└── java.lang.Throwable
├── java.lang.Error
└── java.lang.Exception
├── java.lang.RuntimeException (Unchecked)
└── Other Exceptions (Checked)
  • Error: Represents serious problems that a reasonable application should not try to catch, like OutOfMemoryError or StackOverflowError.
  • Exception: Represents conditions that a reasonable application might want to catch.
    • Checked Exceptions: Subclasses of Exception (but not RuntimeException). The compiler enforces that you handle them using a try-catch block or declare them with throws.
    • Unchecked Exceptions: Subclasses of RuntimeException. The compiler does not enforce handling. These usually indicate programming errors.
KeywordDescription
tryThe block of code to be monitored for exceptions.
catchThe block of code that handles a specific type of exception.
finallyThe block of code that is always executed, whether an exception is thrown or not.
throwUsed to manually throw an exception.
throwsUsed in a method signature to declare the exceptions that might be thrown.
public void processFile(String fileName) {
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Error: Cannot divide by zero.");
// Output: Error: Cannot divide by zero.
} finally {
System.out.println("This block always executes.");
// Output: This block always executes.
// Executes always, regardless of exception thrown or not (unless JVM is terminated).
// Used for resource cleanup (files, sockets).
// If both try and finally have return statements, finally’s return overrides.
}
}
AspectChecked ExceptionUnchecked Exception
Compiler CheckEnforced at compile-time.Not checked at compile-time.
HandlingMust be handled (try-catch) or declared (throws).Handling is optional.
Use CaseFor recoverable conditions (e.g., file not found, network error).For programming errors (e.g., null pointers, invalid arguments).
ExampleIOException, SQLExceptionNullPointerException, IllegalArgumentException

This feature automatically manages resources like files or database connections. Any object that implements java.lang.AutoCloseable can be used.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
System.out.println(br.readLine());
} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
}
// The BufferedReader is automatically closed here.
class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}
  • Manually throw an exception using throw
  • Declaring an exception: throws keyword in method signature
void test(int age) throws InvalidAgeException {
if (age < 0) throw new InvalidAgeException("Negative age!");
}
  • Unhandled exceptions bubble up the call stack until caught or program ends.
  • Can be declared in method signatures for callers to handle.

Exception chaining is the practice of wrapping an exception within another one. This is useful for adding context while preserving the original stack trace.

public void processData() throws DataProcessingException {
try {
// Code that might throw a low-level exception
int[] data = null;
System.out.println(data.length);
} catch (NullPointerException e) {
// Wrap the original exception in a custom, higher-level exception
throw new DataProcessingException("Error processing data due to a null value", e);
}
}
class DataProcessingException extends Exception {
public DataProcessingException(String message, Throwable cause) {
super(message, cause);
}
}

When using try-with-resources, if an exception is thrown in both the try block and when closing the resource, the exception from the try block is thrown, and the other is “suppressed.”

class MyResource implements AutoCloseable {
public void process() {
throw new RuntimeException("Exception in process()");
}
@Override
public void close() throws Exception {
throw new Exception("Exception in close()");
}
}
try (MyResource res = new MyResource()) {
res.process();
} catch (Exception e) {
System.out.println("Primary Exception: " + e.getMessage());
// Output: Primary Exception: Exception in process()
for (Throwable suppressed : e.getSuppressed()) {
System.out.println("Suppressed: " + suppressed.getMessage());
// Output: Suppressed: Exception in close()
}
}

Checked exceptions cannot be thrown directly from a lambda expression unless the functional interface’s method declares it. A common solution is to wrap the checked exception in an unchecked one.

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
List<Integer> numbers = Arrays.asList(1, 2, 0, 4);
numbers.forEach(i -> {
try {
if (i == 0) {
throw new IOException("Checked exception in lambda");
}
System.out.println(10 / i);
} catch (IOException e) {
// Wrap in an unchecked exception
throw new RuntimeException(e);
}
});
  • Be Specific: Catch the most specific exception possible. Avoid catching generic Exception.
    try {
    // code
    } catch (IOException e) {
    // Handle IO exception
    // Specific exceptions should be caught first
    } catch (Exception e) {
    // Handle all others
    // General exceptions should be caught last
    }
  • Don’t Swallow Exceptions: Never leave a catch block empty. At a minimum, log the exception.
  • Use finally for Cleanup: Ensure resources are released, or use try-with-resources.
  • Favor Unchecked Exceptions: For programming errors, unchecked exceptions are generally preferred.
  • Document Exceptions: Use Javadoc’s @throws tag to specify the exceptions a method can throw.
  • Avoid deep nesting of try/catch blocks.

1. What is the difference between throw and throws?

  • throw: A keyword used to manually throw an instance of an exception within a method or block.
  • throws: A keyword used in a method signature to declare the types of checked exceptions that the method might throw.

2. What happens if an exception is thrown from a finally block?

If an exception is already propagating from the try or catch block, it is discarded, and the new exception from the finally block is thrown. This can hide the original cause of the error, so it should be avoided.

3. Can a try block exist without a catch block?

Yes, a try block can be followed by a finally block without any catch blocks. This is useful when you want to ensure cleanup code runs but don’t want to handle any exceptions at this level.

4. What is the difference between final, finally, and finalize()?

  • final: A keyword used to declare that a variable cannot be changed, a method cannot be overridden, or a class cannot be subclassed.
  • finally: A block in a try-catch statement that is always executed.
  • finalize(): A method that the Garbage Collector calls on an object when it determines that there are no more references to the object. Its use is discouraged.