public void setBirthday(String birthDate) throws InvalidBirthdayException { DateFormat formatter = new SimpleDateFormat(); try { Date birthday = formatter.parse(birthDate); } catch (ParseException ex) { throw new InvalidBirthdayException("Date of birth is invalid", ex); } }As you can see in the setBirthday() method, the ParseException is re-thrown under a new exception called InvalidBirthdayException. The ParseException is chained via the constructor of InvalidBirthdayException class:
throw new InvalidBirthdayException("Date of birth is invalid", ex);This custom exception is implemented as following:
public class InvalidBirthdayException extends Exception { public InvalidBirthdayException(String message, Throwable cause) { super(message, cause); } }You can notice that, this constructor invokes its super’s constructor:
super(message, cause);The supertypes of all exceptions Throwable and Exception implement this constructor, so any custom exceptions can call it. The origin exception (the cause) is passed to the being-created exception via its constructor.Remember that the Exception class provides the following constructors that help chaining an exception:
public Throwable initCause(Throwable cause)
That’s how exceptions are chained together. Let’s see another example which is illustrated by the following picture:And following is source code of each class.DAOException.java:public class DAOException extends Exception { public DAOException(String message, Throwable cause) { super(message, cause); } }StudentException.java:
public class StudentException extends Exception { public StudentException(String message) { super(message); } public StudentException(String message, Throwable cause) { super(message, cause); } }DatabaseUtils.java:
import java.sql.*; public class DatabaseUtils { public static void executeQuery(String sql) throws SQLException { throw new SQLException("Syntax Error"); } }StudentDAO.java:
import java.sql.*; public class StudentDAO { public void list() throws DAOException { try { DatabaseUtils.executeQuery("SELECT"); } catch (SQLException ex) { throw new DAOException("Error querying students from database", ex); } } }StudentManager.java:
public class StudentManager { private StudentDAO dao; public StudentManager(StudentDAO dao) { this.dao = dao; } public void findStudents(String keyword) throws StudentException { try { dao.list(); } catch (DAOException ex) { throw new StudentException("Error finding students", ex); } } }StudentProgram.java:
public class StudentProgram { public static void main(String[] args) { StudentDAO dao = new StudentDAO(); StudentManager manager = new StudentManager(dao); try { manager.findStudents("Tom"); } catch (StudentException ex) { ex.printStackTrace(); } } }Run the StudentProgram and you should see the following output:
StudentException: Error finding students at StudentManager.findStudents(StudentManager.java:13) at StudentProgram.main(StudentProgram.java:9) Caused by: DAOException: Error querying students from database at StudentDAO.list(StudentDAO.java:11) at StudentManager.findStudents(StudentManager.java:11) ... 1 more Caused by: java.sql.SQLException: Syntax Error at DatabaseUtils.executeQuery(DatabaseUtils.java:5) at StudentDAO.list(StudentDAO.java:8) ... 2 moreYou see? The printed exception stack trace reveals an exception propagates from the DatabaseUtils layer up to the StudentProgram layer in which the exception is handled by printing this trace.Now, let rewrite, compile and run the example to experiment the exception chaining yourself.