Prerequisites: You will need a basic understanding of the Java call stack.

An exception is simply an abnormality of the code which disrupts the normal flow of execution of the program at runtime.

Normally if you don’t handle exceptions in a program, if the program finds an abnormality at runtime, it will completely stop the execution of the rest of the code. In a large-scale program, this will no be the ideal situation. Therefore, we need to handle these exceptions so that the rest of the code can execute without any interruptions even when there is an exception. That’s why we need Exception Handling.

Java Exception Hierarchy

The following is the Java Exception classes hierarchy. Throwable is the root class and exception and error classes are inherited by the Throwable class.

Java exception hierarchy

Checked vs Unchecked exceptions

Checked exceptions are the exceptions which are checked at compile time. These exceptions must be handled or threw.

Unchecked exceptions are the exceptions which are checked at run time. Most of the times these exceptions occur due to programming errors. Therefore, we should not catch or handle these errors, but we should make sure to write the code without programming errors to prevent these exceptions.

When handling Java exceptions, we use the following keywords.

  1. try
  2. catch
  3. finally
  4. throw — this keyword is used to throw the exception from the method
  5. throws — this keyword indicates that there might be an exception thrown from the method and is used with the method signature (to declare an exception)

The following is the structure of using these keywords.

public returnType functionName(params) throws SomeExceptionClass {
try{
//code which may give an exception
}catch(SomeExceptionClass e){
//Here you can either handle the exception or simply throw it (if you throw the exception, it should get caught and handled at the bottom methods of the call stack)
throw e;
}finally{
//The important code which should run regardless of the try catch block result
}
//rest of the code
return something;
}

NOTE: When you throw an exception always make sure to throw an instance of the Exception class mentioned after the throws keyword in the method signature.

Now what happens here is that if an exception occurs inside the try block, it will get caught from the catch block and will throw an exception. Then it will execute the code block inside finally regardless of the result of the try-catch block. Now even this method throws an error, the rest of the code will execute without any interruptions.

Nested try block

Suppose a situation where your code has an arithmetic part that might give an ArithmeticException and the complete code block which includes that arithmetic code block might give another type of exception. Now we need to handle both of these exceptions and for that, we use a nested try block.

try{
//some code
try{
//code with arithmetic part
}catch(ArithmeticException e){
//exception handling
}
//rest of the code
}catch(SomeOtherException e){
//exception handling
}

NOTE: There should be a catch after every try. A try is always followed by a catch.

Multiple catch blocks

There can be some code lines in your code that can cause different types of exceptions. For such scenarios, we can use multiple catch blocks after the try block to handle different exceptions separately. Whenever an exception occurs, it immediately executes the relevant catch block. Therefore, only one catch block out of all the catch blocks will be executed at a single run.

After seeing the Java exception hierarchy, one might think as all the subclasses inherit the Exception superclass, why do we even bother to catch the exceptions separately using multiple catch blocks without catching all of them at once using single Exception class catch block. The issue of doing that is, the only information we can gather from that is just “something went wrong” and it makes it really difficult to handle such exceptions without being more specific about the exception. Therefore, we should only use the Exception class at the bottom of the other catch blocks as a last resort.

try{
//some code
{catch(ExceptionTypeOne ex1){
//handling exception
}catch(ExceptionTypeTwo ex2){
//handling exception
}
.
.
.

IMPORTANT: When we put multiple catch blocks, we need to make sure that the catch blocks are in the order of exception subclass to exception superclass. To order the catch blocks we can use the exception hierarchy chart.

Let me explain this with an example.

As you can see in the Java exception hierarchy chart, ArrayIndexOutOfBoundException is a subclass of the IndexOutOfBoundException class which is a subclass of the RuntimeException class which is a subclass of the Exception class. Now when handling exceptions using multiple catch blocks, you should do it as follows.

try{
//some code
}catch(ArrayIndexOutOfBound ex1){
//handle array index out of bound
}catch(IndexOutOfBound ex2){
//handle index out of bound
}catch(RuntimeException ex3){
//handle runtime exception
}catch(Exception ex4){
//handle exception
}

Now, if you put Java Exception class catch block as the topmost catch block, whenever there is an exception, regardless of whether it is an IndexOutOfBoundException or a RuntimeException, it will be handled by the Exception catch block without checking other catch blocks as it is at the top. So our exception will be more general than being specific. Therefore we always have to follow the proper order when we have multiple catch blocks.

Exception Propagation

Now the question is what happens once an exception is thrown?

When a method throws an exception, that exception will be propagated down the call stack (from child method to parent method) until it gets caught and handled or it reaches the very bottom of the call stack.

Custom Exceptions

Other than these built-in Exception classes, you can create your own Exception class to handle the exceptions the way you want. let’s see a very basic example for this.

First, you have to define your custom exception class.

public class CustomException extends Exception{
//constructor
public CustomException(String customErrorMessage){
//passing the custom error message to super class (Exception class) constructor
super(customErrorMessage)
}
}

Now using this custom exception class we created, we can pass our own error message rather than passing the built-in error message.

Now we have to define a situation when to throw this type of exception.

public returnType functionNameABC(params) throws CustomException{
//some code
if(*condition when to throw the custom exception){
throw new CustomException("My custom error message");
}
}

Now our custom exception will work as same as the other exceptions and will throw an exception when the given condition* is met.

try{
functionNameABC(params);
}catch(CustomException e){
//handle custom exception
}

In this article, we have discussed Java Exception Handling basics. Hope this article is helpful to the people who are trying to understand these concepts. Cheers!

Software Engineering undergraduate @University of Colombo School of Computing (UCSC)