Posted on: January 18, 2025 Posted by: rahulgite Comments: 0

Exception handling is a mechanism in Java to handle runtime errors, ensuring normal application flow.


1. Exception Hierarchy in Java

The Throwable class is the root of Java’s exception hierarchy.

Throwable
├── Exception
│   ├── IOException
│   ├── SQLException
│   ├── RuntimeException
│       ├── NullPointerException
│       ├── ArrayIndexOutOfBoundsException
│       ├── ArithmeticException
├── Error
    ├── StackOverflowError
    ├── OutOfMemoryError
  • Checked Exceptions: Must be declared in the throws clause or handled using try-catch. Examples: IOException, SQLException.
  • Unchecked Exceptions: Subclasses of RuntimeException and do not require explicit handling. Examples: NullPointerException, ArithmeticException.
  • Errors: Represent serious problems that applications should not catch. Examples: OutOfMemoryError, StackOverflowError.

2. Exception vs. Error

FeatureExceptionError
DefinitionRecoverable conditions in the program.Critical issues not meant to be handled.
HierarchySubclass of Throwable.Subclass of Throwable.
ExamplesIOException, SQLException.StackOverflowError, OutOfMemoryError.
HandlingCan be handled with try-catch.Should not be handled in most cases.

3. Can We Write try Without catch and finally?

  • Yes, but it must include a try with a finally block.
  • Example:
try {
    System.out.println("Try block");
} finally {
    System.out.println("Finally block");
}

4. Can We Add Statements Between try, catch, and finally?

  • No, statements cannot be placed between try, catch, and finally blocks.
  • Invalid Example:
try {
    // Code
}
System.out.println("Invalid"); // Compilation error
catch (Exception e) {
    // Code
}

5. Does the Code After try Execute If an Exception Occurs?

  • No, if an exception occurs in the try block, the remaining code in the try block does not execute.
  • Example:
try {
    System.out.println("Before exception");
    int result = 10 / 0; // Exception occurs here
    System.out.println("After exception"); // Skipped
} catch (ArithmeticException e) {
    System.out.println("Exception caught");
}

6. throw vs. throws

Featurethrowthrows
PurposeUsed to explicitly throw an exception.Declares exceptions a method might throw.
PositionUsed inside a method.Declared in the method signature.
Syntaxthrow new Exception();void method() throws Exception {}

Example of throw

void validateAge(int age) {
    if (age < 18) {
        throw new IllegalArgumentException("Age must be 18 or above.");
    }
}

Example of throws

void readFile() throws IOException {
    FileReader fr = new FileReader("file.txt");
}

7. What Happens If an Exception is Thrown by main?

  • If an exception is thrown and not handled in main, it propagates to the JVM, which terminates the program and prints the stack trace.
  • Example:
public static void main(String[] args) {
    throw new RuntimeException("Unhandled exception");
}

8. Unreachable Code in Exception Handling

  • Definition: Code that cannot be executed due to improper exception handling.
  • Example:
try {
    System.out.println("Try block");
    return;
} catch (Exception e) {
    System.out.println("Catch block");
} finally {
    System.out.println("Finally block");
}
System.out.println("Unreachable"); // Compilation error

9. Multi-Catch Blocks

  • Definition: A single catch block can handle multiple exception types (introduced in Java 7).
  • Example:
try {
    int result = 10 / 0;
    String s = null;
    s.length();
} catch (ArithmeticException | NullPointerException e) {
    System.out.println("Exception: " + e.getMessage());
}
  • Rules:
    • Exception types must not have a parent-child relationship.

10. Custom Exceptions

  • Definition: Custom exceptions are user-defined exceptions that extend Exception or RuntimeException.
  • Use Case: When specific exceptions related to business logic need to be defined.

Example: Custom Exception

class AgeException extends Exception {
    public AgeException(String message) {
        super(message);
    }
}

public class CustomExceptionDemo {
    public static void main(String[] args) {
        try {
            validateAge(15);
        } catch (AgeException e) {
            System.out.println("Exception: " + e.getMessage());
        }
    }

    static void validateAge(int age) throws AgeException {
        if (age < 18) {
            throw new AgeException("Age must be at least 18.");
        }
    }
}

11. Nested Try-Catch Blocks

  • Definition: Try-catch blocks can be nested for handling different exceptions in different sections of code.
  • Example:
public class NestedTryCatch {
    public static void main(String[] args) {
        try {
            try {
                int result = 10 / 0; // ArithmeticException
            } catch (ArithmeticException e) {
                System.out.println("Inner catch: " + e.getMessage());
            }
            String str = null;
            str.length(); // NullPointerException
        } catch (NullPointerException e) {
            System.out.println("Outer catch: " + e.getMessage());
        }
    }
}

12. Chained Exceptions

  • Definition: Allows one exception to be associated with another exception.
  • Use Case: To provide more context about the root cause of an exception.
  • Methods:
    • getCause(): Retrieves the original exception.
    • initCause(Throwable cause): Associates a cause with an exception.

Example: Chained Exception

public class ChainedExceptionDemo {
    public static void main(String[] args) {
        try {
            throw new ArithmeticException("Division error").initCause(new NullPointerException("Root cause: Null value"));
        } catch (ArithmeticException e) {
            System.out.println("Exception: " + e.getMessage());
            System.out.println("Cause: " + e.getCause());
        }
    }
}

13. Final Thoughts on Best Practices

  1. Handle Exceptions at the Right Level:
    • Catch exceptions where they can be meaningfully resolved.
  2. Avoid Catching Generic Exceptions:
    • Do not use catch (Exception e) unless absolutely necessary.
  3. Log Exceptions:
    • Always log exceptions for debugging and tracking purposes.
  4. Use Finally for Resource Cleanup:
    • Use finally or try-with-resources to release resources.
  5. Avoid Empty Catch Blocks:
    • Always handle exceptions properly to avoid masking issues.

Conclusion

Understanding Java’s exception hierarchy and handling mechanisms helps write robust and error-resilient applications. Concepts like custom exceptions, nested try-catch, chained exceptions, and best practices ensure effective management of runtime issues.

Leave a Comment