In this post, you will learn to:

  • Explain user-defined exceptions.
  • Explain implementation of an user-defined exceptions.
  • Explain handling and throwing of user-defined exceptions.
  • Exception chaining.

User-defined Exceptions

User-defined exceptions are custom exceptions. These exceptions are created when predefined exceptions are not sufficient to handle situations specific to an application. User-defined exception classes are subclassed from the base class Exception. These exceptions are handled and thrown in the same manner as predefined exceptions.

Creating User-defined Exceptions

The Exception class is the base class of all user-defined exceptions. User-defined exceptions provide business solutions that can be used in the programs. The Throwable class is the super class of all exceptions and errors in the Java language. All the methods of the Throwable class are inherited by the Exception class, since Exception is a subclass of Throwable class. Hence, user-defined exception classes can use all the methods of the Throwable class.

The following is the syntax to create user-defined exception class.

// any user defined exception has to extend base class
class MyException extends Exception{
// exception-handling code
}

The following code demonstrates the declaration of user-defined exception class.

public class InvalidPriceException extends Exception{
   // class variables
   private String exceptionMesg;
   //Default Constructor 
   public InvalidPriceException(){
      exceptionMesg="";
   }
   
   // Constructor with one arguement
   public InvalidPriceException(String str){
      exceptionMesg=str;
   }
 
   // overrides base class method
   public String getMessage()
   {
      return exceptionMesg;
   }
}

Throwing User-defined Exceptions

It is important for a developer to know when to make use of exceptions. There may be a tendency to excessively use the Exception API, just because it is convenient. It is important to note that every time an exception is thrown the execution of the program is hampered. The only guideline to follow for deciding when to throw an exception is that if a method encounters an abnormal condition that it can’t handle, it should throw an exception.This raises another question about what can be classified as an ‘abnormal condition’.

An abnormal condition, therefore, would be any condition that wouldn’t reasonably be expected as part of the ‘normal functioning’ of a method. Exceptions can be used to test whether the parameters to a public method or public constructor are legal.

The following is the syntax to throw user defined exceptions.

where,
ExceptionType is an instance of a user-defined exception class subclassed from Throwable class.

The following code demonstrates the throwing of a user-defined exception.

void setPrice(float itemPrice) throws InvalidPriceException{
   if (itemPrice <0) throw new InvalidPriceException();
   price=itemPrice;
   } 

If negative price has been entered, the method setPrice throws an exception.

Output:

InvalidPriceException: Invalid Price
  at CalculatePrice.setPrice(CalculatePrice.java:33)
   CalculatePrice.main(CalculatePrice.java:41)

Note: The client code that will invoke the method will handle the exception using try-catch block or rethrow it in case it does not want to handle it.

Exception Chaining

Exception chaining, or exception wrapping, is an object-oriented programming technique of handling exceptions by re-throwing a caught exception after wrapping it inside a new exception. The original exception is saved as a property (such as cause) of the new exception. The idea is that a method should throw exceptions defined at the same abstraction level as the method itself, but without discarding information from the lower levels.

The following code shows the common method to catch one exception and throw another in the Java code.

Code Snippet:

try {
 ...
} catch(YourException e) {
    throw new MyException();
}

If the information from the original exception is lost, debugging will become impossible. Hence, while wrapping exceptions, an accessor is generally provided to extract the contained exception. This allows developers to construct chains of exceptions consisting of wrapped exceptions.

The advantages of using the exception chaining facility are as follows:

  • The fact that one exception caused another can be recorded, regardless of what the exceptions are.
  • Since a common API is used to record the fact that one exception caused another, it encourages programmers to keep a record of the exception chain.
  • The record that a particular exception caused another exception can be stored and referred to at a later time.

To keep a record of the exception chain, two methods of the Throwable class are used. The getCause() and initCause(Throwable), and the two constructors, Throwable(Throwable) and Throwable(String, Throwable) are used to record the chain of exceptions. Other ‘general purpose’exception classes (like Exception, RunTimeException and Error) have been similarly outfitted with (Throwable) and (String, Throwable) constructors. However, even exceptions without such constructors can be used as ‘wrapped exceptions’ by using the initCause() method.

The implementation of Throwable.printStackTrace has been modified to display back traces for the entire causal chain of exceptions. New method getStackTrace() provides programmatic access to the stack trace information provided by printStackTrace.