Courses/Java/Exception Architecture

    Lesson 19 โ€ข Advanced

    Exception Handling Architecture

    Custom exception hierarchies, chained exceptions, and error strategies for large systems.

    ๐Ÿ“š Before You Start

    You should understand:

    • Exception Handling (Lesson 12) โ€” try-catch-finally, throw/throws
    • OOP & Inheritance (Lessons 9-10) โ€” class hierarchies
    • Interfaces (Lesson 11) โ€” contracts and polymorphism

    What You'll Learn

    • โœ… Custom exception hierarchies for your domain
    • โœ… Chained exceptions (cause wrapping)
    • โœ… Checked vs unchecked exception strategy
    • โœ… Global exception handlers
    • โœ… Result/Either pattern as an alternative
    • โœ… Enterprise error handling best practices

    1๏ธโƒฃ Designing Exception Hierarchies

    ๐Ÿ’ก Analogy: Hospital Triage โ€” A base AppException is the intake form. ValidationException is a minor injury. DatabaseException needs specialist attention. NotFoundException is common and expected. Each type gets routed to the right handler.

    Modern rule: Use unchecked (RuntimeException) for most cases. Use checked only when the caller can reasonably recover.

    // Domain exception hierarchy
    public class AppException extends RuntimeException {
        private final String code;
        public AppException(String message, String code, Throwable cause) {
            super(message, cause);
            this.code = code;
        }
    }
    
    public class ValidationException extends AppException { /* field info */ }
    public class NotFoundException extends AppException { /* entity info */ }
    public class DatabaseException extends AppException { /* retry info */ }

    Try It: Custom Exception Hierarchy

    Build a domain-specific exception system with error codes and context

    Try it Yourself ยป
    JavaScript
    // ๐Ÿ’ก Try modifying this code and see what happens!
    // Custom Exception Hierarchy (Simulated)
    console.log("=== Exception Hierarchy ===\n");
    
    class AppException extends Error {
        constructor(message, code, cause) {
            super(message);
            this.name = "AppException";
            this.code = code;
            this.cause = cause;
            this.timestamp = new Date().toISOString();
        }
    }
    
    class ValidationException extends AppException {
        constructor(field, message) {
            super(message, "VAL
    ...

    2๏ธโƒฃ Chained Exceptions

    Always preserve the root cause when wrapping exceptions. Pass the original exception as the cause parameter โ€” this creates a chain that helps debugging.

    // โœ… Good โ€” preserves root cause
    catch (SQLException e) {
        throw new DatabaseException("Failed to fetch user", e);
    }
    
    // โŒ Bad โ€” loses the stack trace
    catch (SQLException e) {
        throw new DatabaseException(e.getMessage()); // cause lost!
    }

    3๏ธโƒฃ The Result Pattern

    For expected failures (validation, parsing), consider returning a Result<T> instead of throwing. This makes error handling explicit and avoids the performance cost of stack trace creation.

    // Instead of throwing for expected failures:
    Result<User> result = userService.findById(id);
    if (result.isSuccess()) {
        User user = result.getValue();
    } else {
        String error = result.getError();
    }

    Try It: Exception Chaining & Result Pattern

    Practice preserving root causes and using Result instead of throw

    Try it Yourself ยป
    JavaScript
    // ๐Ÿ’ก Try modifying this code and see what happens!
    // Chaining & Result Pattern (Simulated)
    console.log("=== Exception Chaining ===\n");
    
    class AppException extends Error {
        constructor(message, code, cause) {
            super(message); this.code = code; this.cause = cause;
        }
    }
    class DatabaseException extends AppException {
        constructor(message, cause) { super(message, "DB_ERROR", cause); }
    }
    
    function connectToDatabase() {
        throw new Error("Connection refused: port 5432");
    }
    
    functio
    ...

    4๏ธโƒฃ Global Exception Handlers

    In enterprise apps, you centralize error handling. Spring Boot uses @ControllerAdvice to map exception types to HTTP responses automatically.

    @ControllerAdvice
    public class GlobalExceptionHandler {
        @ExceptionHandler(NotFoundException.class)
        ResponseEntity<?> handleNotFound(NotFoundException e) {
            return ResponseEntity.status(404).body(e.getMessage());
        }
        
        @ExceptionHandler(ValidationException.class)
        ResponseEntity<?> handleValidation(ValidationException e) {
            return ResponseEntity.status(400).body(e.getMessage());
        }
    }

    Try It: Global Error Handler System

    Build a centralized error handler that routes exceptions to the right handler

    Try it Yourself ยป
    JavaScript
    // ๐Ÿ’ก Try modifying this code and see what happens!
    // Global Error Handler (Simulated)
    console.log("=== Global Error Handler ===\n");
    
    class AppException extends Error {
        constructor(message, code) { super(message); this.code = code; }
    }
    class ValidationException extends AppException {
        constructor(field, message) { super(message, "VALIDATION"); this.field = field; this.name = "ValidationException"; }
    }
    class NotFoundException extends AppException {
        constructor(entity, id) { super(ent
    ...

    5๏ธโƒฃ Common Mistakes

    โŒ
    Catching Exception (too broad): Swallows everything including bugs. Catch the most specific type possible.
    โŒ
    Losing the cause: throw new MyEx(e.getMessage()) loses the stack trace. Always pass the original: new MyEx("context", e).
    โŒ
    Using exceptions for flow control: Throwing for "user not found" is expensive. Use Optional or Result for expected cases.
    โŒ
    Empty catch blocks: catch(Exception e) {} silently swallows errors. At minimum, log the exception.

    6๏ธโƒฃ Pro Tips

    ๐Ÿ’ก Include error codes in exceptions to enable automatic API response mapping.

    ๐Ÿ’ก Create a base exception per module: PaymentException, OrderException. Callers can catch an entire module's errors.

    ๐Ÿ’ก In Spring Boot, use @ControllerAdvice with @ExceptionHandler for centralized error handling.

    ๐Ÿ“‹ Quick Reference

    ConceptJava SyntaxWhen to Use
    Custom exceptionextends RuntimeExceptionDomain-specific errors
    Chained exceptionnew Ex(msg, cause)Preserve root cause
    Checkedextends ExceptionRecoverable errors
    Uncheckedextends RuntimeExceptionProgramming errors
    Result patternResult.ok(v) / .fail(e)Expected failures

    ๐ŸŽ‰ Lesson Complete!

    You can now design robust error handling systems for enterprise Java applications!

    Next: Java Generics Advanced โ€” bounded types, wildcards, and type erasure.

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy Policy โ€ข Terms of Service