C

Core Java tutorial for beginners

Clean • Professional

Exception Chaining in Java – Explanation with Examples

4 minute

Exception Chaining in Java

Exception Chaining is a mechanism in Java that allows you to link one exception to another, so you can throw a new exception while preserving the original cause.

This is extremely useful when a low-level exception should not be exposed directly, but its root cause must still be traceable.


What is Exception Chaining?

Throwing a higher-level exception while keeping the original (root) exception attached as the cause.

This ensures:

  • The real underlying problem is preserved
  • The user or developer receives a meaningful message
  • Debugging becomes much easier
  • Multi-layered applications can pass errors upwards cleanly

Java supports this using:

  • Throwable(Throwable cause) constructor
  • initCause(Throwable cause) method
  • getCause() method

Why Do We Use Exception Chaining?

  • To preserve the original cause of an exception
  • To add more context while throwing a new exception
  • To make debugging easier
  • Best practice in layered applications (Controller → Service → DAO)

How Exception Chaining Works

learn code with durgesh images

1. Throwable(Throwable cause) Constructor

  • This is a constructor (not a method).
  • Used to create an exception along with its cause.
throw new Exception("Top level exception", new NullPointerException("Root cause")

2. initCause(Throwable cause) Method

  • This is a method.
  • Used when you want to set the cause after creating the exception object.
Exception ex = new Exception("Top level exception");
ex.initCause(new ArithmeticException("Root cause"));
throw ex;

3. getCause() Method

  • Returns the cause of the exception (the chained exception).
  • Useful for logging and debugging.
try {
    // code that throws exception
} catch (Exception e) {
    System.out.println("Cause: " + e.getCause());
}

Basic Exception Chaining Example

public class Test {
    public static void main(String[] args) {
        try {
            divide();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void divide() throws Exception {
        try {
            int num = 10 / 0;  // Low-level exception
        } catch (ArithmeticException e) {
            throw new Exception("Error occurred while dividing", e); // Chaining
        }
    }
}

Output

java.lang.Exception: Error occurred while dividing
Caused by: java.lang.ArithmeticException: / by zero

Exception Chaining With Custom Exception

class InvalidDataException extends Exception {
    public InvalidDataException(String message, Throwable cause) {
        super(message, cause);
    }
}

public class Main {
    public static void process() throws InvalidDataException {
        try {
            String s = null;
            s.length(); // NullPointerException
        } catch (NullPointerException e) {
            throw new InvalidDataException("Data processing failed", e);
        }
    }

    public static void main(String[] args) {
        try {
            process();
        } catch (InvalidDataException e) {
            e.printStackTrace();
        }
    }
}

Example Using initCause()

Exception ex = new Exception("Top level error");
ex.initCause(new NullPointerException("Root cause error"));
throw ex;

Real-World Example (Layered Architecture)

We will see how a request flows through:

learn code with durgesh images

Controller Layer → Service Layer → DAO Layer → Database

1. Controller Layer (Handles Requests)

The controller receives the user’s request (e.g., register a new user).

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/register")
    public String registerUser(@RequestBody User user) {
        userService.registerUser(user); // call service layer
        return "User registered successfully!";
    }
}
  • Accepts request
  • Passes data to Service Layer
  • Returns response

2. Service Layer (Business Logic)

The service layer performs business rules such as validations.

@Service
public class UserService {

    @Autowired
    private UserDAO userDAO;

    public void registerUser(User user) {

        // Business rules
        if (user.getEmail() == null || user.getEmail().isEmpty()) {
            throw new IllegalArgumentException("Email cannot be empty");
        }

        // Password rule
        if (user.getPassword().length() < 6) {
            throw new IllegalArgumentException("Password must be at least 6 characters");
        }

        // Call DAO
        userDAO.save(user);
    }
}
  • Validates user
  • Applies business rules
  • Communicates with DAO Layer

3. DAO Layer (Database Operations)

The DAO layer actually interacts with the database.

@Repository
public class UserDAOImpl implements UserDAO {

    @Override
    public void save(User user) {
        // Example: JDBC code
        String sql = "INSERT INTO users (name, email, password) VALUES (?, ?, ?)";

        // Database logic here…
        System.out.println("User saved to database: " + user.getName());
    }

    @Override
    public User findById(int id) {
        // fetch from DB (logic not shown)
        return new User("Demo User", "[email protected]", "123456");
    }
}
  • Handles CRUD operations
  • Talks directly to the database

4. Database Layer

Actual tables stored in MySQL / Oracle / PostgreSQL.

users table

idnameemailpassword
1John Doe[email protected]*****

End-to-End Data Flow (Very Easy)

User → sends registration request
       ↓
Controller Layer → receives & forwards request
       ↓
Service Layer → validates data & applies business rules
       ↓
DAO Layer → saves data to database
       ↓
Database → stores the information
       ↓
Result → Success response is returned to user

How JVM Displays Chained Exceptions

java.lang.Exception: Top level error
    at ...
Caused by: java.lang.NullPointerException: Root cause error
    at ...

This “Caused by” line is the chained exception.


Benefits of Exception Chaining

BenefitExplanation
Preserves Root CauseNever lose the original error
Better DebuggingFull trace from top layer to root
Clear ContextEach layer adds meaningful messages
Best PracticeEssential for enterprise applications
Cleaner CodeReduces confusion and error hiding

Key Methods Used in Exception Chaining

MethodPurpose
getCause()Returns original cause
initCause(Throwable cause)Sets cause manually
Constructor with causenew Exception("msg", cause)

When to Use Exception Chaining?

Use it in:

  • Database operations
  • File handling
  • Network communication
  • Business logic layers
  • APIs and web applications
  • Custom exceptions

Article 0 of 0