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.
The Exception Hierarchy
Section titled “The Exception Hierarchy”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, likeOutOfMemoryErrororStackOverflowError.Exception: Represents conditions that a reasonable application might want to catch.- Checked Exceptions: Subclasses of
Exception(but notRuntimeException). The compiler enforces that you handle them using atry-catchblock or declare them withthrows. - Unchecked Exceptions: Subclasses of
RuntimeException. The compiler does not enforce handling. These usually indicate programming errors.
- Checked Exceptions: Subclasses of
Core Keywords
Section titled “Core Keywords”| Keyword | Description |
|---|---|
try | The block of code to be monitored for exceptions. |
catch | The block of code that handles a specific type of exception. |
finally | The block of code that is always executed, whether an exception is thrown or not. |
throw | Used to manually throw an exception. |
throws | Used 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. }}Checked vs. Unchecked Exceptions
Section titled “Checked vs. Unchecked Exceptions”| Aspect | Checked Exception | Unchecked Exception |
|---|---|---|
| Compiler Check | Enforced at compile-time. | Not checked at compile-time. |
| Handling | Must be handled (try-catch) or declared (throws). | Handling is optional. |
| Use Case | For recoverable conditions (e.g., file not found, network error). | For programming errors (e.g., null pointers, invalid arguments). |
| Example | IOException, SQLException | NullPointerException, IllegalArgumentException |
try-with-Resources Java 7+
Section titled “try-with-Resources Java 7+”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.Advanced Topics
Section titled “Advanced Topics”Creating custom exceptions
Section titled “Creating custom exceptions”class InvalidAgeException extends Exception { public InvalidAgeException(String message) { super(message); }}Throwing Exceptions
Section titled “Throwing Exceptions”- 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!");}Exception Propagation
Section titled “Exception Propagation”- Unhandled exceptions bubble up the call stack until caught or program ends.
- Can be declared in method signatures for callers to handle.
Exception Chaining
Section titled “Exception Chaining”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); }}Suppressed Exceptions Java 7+
Section titled “Suppressed Exceptions Java 7+”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() }}Handling Exceptions in Lambdas Java 8+
Section titled “Handling Exceptions in Lambdas Java 8+”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); }});Best Practices
Section titled “Best Practices”- 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
catchblock empty. At a minimum, log the exception. - Use
finallyfor Cleanup: Ensure resources are released, or usetry-with-resources. - Favor Unchecked Exceptions: For programming errors, unchecked exceptions are generally preferred.
- Document Exceptions: Use Javadoc’s
@throwstag to specify the exceptions a method can throw. - Avoid deep nesting of try/catch blocks.
Conceptual Questions and Answers
Section titled “Conceptual Questions and Answers”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 atry-catchstatement 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.