Welcome! This site is currently in beta. Get 10% off everything with promo code BETA10.

Blog Post

An Introductory Guide to Exception Handling in Java

33 min to complete · By Ryan Desmond

Exceptions (and Errors) in our programs are common. Java exceptions can be caused by incorrect syntax, faulty logic, incorrect user input, a server offline, a network issue, etc. Typically, when our programs encounter an exception, they will immediately terminate execution and cause our applications to crash. This is obviously suboptimal, and nobody wants that. But, we have good news… Introducing exception handling in Java!

Exception handling in Java programmatically handles runtime errors in order to avoid disrupting the program’s normal flow. By using exception handling, we can build our programs to take appropriate actions and keep our applications from crashing when an error or exception occurs.

Java makes use of five primary keywords for exception handling: try, catch, finally, throw and throws. In the following sections, we will analyze the importance of each keyword. We’ll then discuss multiple facets of Java exception handling, including the Exception Hierarchy in Java, how to throw an exception in Java, custom exceptions in Java, and more.

Java Exception Handling Keywords

In this section we’ll briefly introduce the primary concepts before diving a bit deeper later in this tutorial.

try:

A “try block” is used when we know an exception may occur in this block. For instance, when creating a connection to a remote database or service we’ll wrap that code in a try block knowing that an exception may be thrown if the database or network is down. It is important to note that a “try block” cannot be used without a corresponding “catch block”.

try { 
   // you put the code that may generate an exception here 
}

catch:

A “catch block” is used to “catch” and handle exceptions encountered within a try block. To “handle” an exception just means to do indicate what we would like to do when the exception is encountered rather than have the program crash.

try { 
   // code here that may generate exception 
} catch (Exception e){ 
   // handle your exception gracefully 
   // how you handle it will change on a case by case basis 
}

finally:

The “finally block” is a block of code that is executed after a try/catch statement no matter what. Whether the code in the try block executes without generating any exceptions, or whether an exception was generated and the catch block was invoked – the Finally block will be executed.

try { 
   // some code that might generate an exception 
} catch (Exception e){ 
   // handle that exception if it is encountered 
} finally { 
   // this code will execute whether or not an exception occurred 
}

throw:

The keyword throw is used to indicate that we would like to “throw” a specific exception to the calling method rather than handle it locally. It is used to manually “throw” an exception.

For instance, let’s say we have a method that sends a text message, and that this text-sending method is invoked by many other methods in your application. Different methods may want to do different things if/when the text-sending method fails. One may want to send an email, one may want to log an error to the database, another may want to alert support staff.

So rather than handling the exception within the text-sending method, the text-sending method will “throw” the exception back to the calling method, and the calling method can handle it the way that it sees fit.

if(!userInput.isNumeric()){ 
   // on the line below we are manually "throwing" an exception 
   throw new InvalidInputException("input must be numeric"); 
}

throws:

The keyword throws is used in method signatures to indicate that this method has the potential to throw one or more given exceptions. For example:

public static void connectToDB throws ConnectionException { 
   // code here to connect to DB which can throw an exception 
   // in this case, the method "throws" the exception to the 
   // calling method rather than handling it locally 
}

Uncaught Java Exceptions:

When an exception is not caught or handled “gracefully”, it is the JVM’s responsibility to handle the error. The issue is that the JVM defaults to terminating the program and printing the stack trace and error message to the log file when an exception or error occurs when such problems are not caught or handled by our applications. For example:

// create an array with 6 elements int[] nums = {1,2,3,4,5,6}; 
// try to print out the 7th element (an Exception will occur) 
System.out.println(nums[7]);
}

Stack Trace: Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 7

This information is often informative and can lead to the source of the issue, however, it is not something you want a user to see. You also do not want an error like this to terminate the application. The above Exception message is thrown when the program tries to access an index in an array that does not exist. Catching an exception like this would give us the option to alert the user (if necessary), or take whatever action we deem necessary, and continue on with the program. We will examine how to handle such exceptions and improve the usability of our code.

Exception Hierarchy in Java

Java’s exceptions are all subclasses of the Throwable class – the top of the exception hierarchy in Java. When an exception occurs, an object of the Throwable class is generated. The two subclasses of Throwable are Error and Exception. Exceptions of type Error are generally caused by the machine, thus we will not examine them. Exceptions of type Exception are caused by our program activity. We will examine various subclasses of Exception and how to handle them.

In the image above you can see that there are “checked” and “unchecked” exceptions. “Checked” simply means that these types of exceptions are “checked” by the compiler at build time. There are also unchecked RuntimeExceptions.

A good example here is an ArrayIndexOutOfBoundsException. This is cannot be checked at compile time as the array that we are accessing is not necessarily available when the application is compiled. However, at run-time it is quite possible that your code can try to access an array index that does not exist. This would be considered a “RuntimeException”.

The best example of an “Error” is the classic StackOverflowError. This happens when your machine runs out of memory on the Stack. (This happens all the time with recursion.) This is also an unchecked error. It is not something the compiler can detect when the application is being compiled.

Built-in Java Exceptions

Java includes several built-in exception packages in the java.lang package. Many of the exceptions we use and/or encounter on a day-to-day basis are subclasses of the RuntimeException class. These are automatically available in any Java class.

Here’s a list of a few common Java exceptions:

  • ArithmeticException Arithmetic error (divide-by-zero)
  • ArrayIndexOutOfBoundsException Array index is out-of-bounds
  • IllegalArgumentException Illegal argument used to call a method
  • IllegalThreadStateException Requested operation not compatible with current thread state
  • IndexOutOfBoundsException Some type of index is out-of-bounds
  • NullPointerException Invalid use of a null reference
  • NumberFormatException Invalid conversion of a string to a numeric format
  • StringIndexOutOfBoundsException Attempt to index outside the bounds of a string
  • TypeNotPresentException Type not found

Multiple Catch Statements:

The catch clause must specify the type of exception that could be thrown inside the try block. It is possible to catch multiple exceptions using one try block. This requires a catch statement for each exception that could be thrown.

Multiple catch statements can be used for one try block if there is a chance two different exceptions could be thrown. For example, if you were trying to divide two numbers in two indexes of an array, there is a chance an ArithmeticException or an ArrayIndexOutOfBounds exception could be thrown. Having a catch statement for each allows the program to handle one or the other.

int[] nums = {3,0,7,9};

try{
   // block of code to monitor for errors
   // this will throw an ArithmeticException
   int x = nums[0] / nums[1]; 
   // this would throw an ArrayIndexOutOfBoundsException
   // int y = nums[10]; 
} catch (ArithmeticException one){
   // handle for Exception one
} catch(ArrayIndexOutOfBoundsException two){
   // handle for Exception two
} catch (Exception e){
 // catch any other exception that is not an ArithmeticException
 // or an ArrayIndexOutOfBoundsException
}

Subclass Exceptions:

When handling multiple exceptions, it is important to remember that the subclass must be caught before the superclass. For example, an ArrayIndexOutOfBoundException (which is a subclass of Exception) must be caught before Exception in the catch hierarchy. This is because Exception will catch all of its subclasses exceptions, but will not give the detail needed that the ArrayIndexOutOfBoundException could provide.

try{
   // block of code to monitor for errors 
} catch (Exception e){
  // both exceptions below are subclasses of the Exception class
  // therefore, the Exception "catch block" will catch them
  // and the two "catch blocks" below are unreachable
} catch (ArithmeticException one){
   // THIS WILL NOT WORK!
} catch(ArrayIndexOutOfBoundsException two){
   // THIS WILL NOT WORK!
} 

Nested Try Blocks:

Try/catch blocks can be nested within each other. If an exception is not caught within the nested try block, the outer try block is able to catch it. Outer blocks can be used as another default option. For example, an inner try block will try to handle an error gracefully but if it cannot, the outer block can catch it and terminate the program.

try{
   // block of code to monitor for errors
} catch (Exception one){
   // handle for Exception one
   try {
       // block of code - nested try
   } catch {
       // handle any exception that occur inside the nested try
   }
}

Try/Catch/Finally:

The finally keyword is used to specify a block of code that executes after a try/catch block no matter the result of the Try/Catch block. For example, if a file is opened inside a try block and an error occurs, the connection can be closed in a finally block because it is something that needs to happen no matter what. The “finally block” will be executed whether an exception was generated in the “try block” or not.

try{
   // code that needs to be monitored for errors
} catch (Exception exc){
   // handler for Exception object exc
} finally{
   // code to execute no matter the result of try/catch
}

As soon as the last line of code in the try/catch block executes, whether it is in the try or the catch, the finally block then executes.

How to throw an exception in Java

1) Run the REPL (Read-Evaluate-Print-Loop) below as-is.

Run it by clicking the triangle “play” button.

Do you see the exception thrown?

Does “Program Complete” print?

class Main {
   public static void main(String[] args) {
    
   int[] nums = {67, 43};

   // try {
      System.out.println(nums[5]);
   // } catch (ArrayIndexOutOfBoundsException exc){
   //   System.out.println("Error detected. Sorry bout that!");
   // }

      System.out.println("Program complete");
   }
}

2) Uncomment the try/catch block and run again

(This is 4 lines that start with “//”)

See the difference?

Did “Program complete” print?

3) Try to generate a new exception and catch it properly

Test this REPL by first running it as is. Notice that “Program complete” will print when an exception is caught. Then, comment out line 13 and uncomment line 16. Notice how “Program complete” will print when no error is found. This demonstrates that the finally block will execute no matter what. If an exception is thrown/caught it will execute. If no exception occurs it will also execute.

class Main {
  public static void main(String[] args) {
    
    int[] nums = {67, 43};

    try {
      
      // comment line below after first run
      System.out.println(nums[1]);

      // uncomment line below after first run
      //System.out.println(nums[5]);

      /* notice how "Program complete" will print no matter what. 
         Whether an error happens or not, the finally block 
         will be executed */
    
    } catch (ArrayIndexOutOfBoundsException exc){
      System.out.println("Error detected. Sorry bout that!");
      System.out.println(exc.getMessage());
    } finally {
      System.out.println("Program complete");
    }    
  }
}

Adding “throws” to the Method Signature:

If a method generates an exception that it does not handle, the method must include a “throws exception” clause in the method signature. This is a common occurrence. Oftentimes, the method where the exception occurs does not know what it should do if/when an exception occurs. In this case, we can “throw” the exception back to the calling method and let the calling method deal with it as it pleases.

When throwing exceptions in Java, a throws clause looks like the following:

public void exampleMethod() throws ArithmeticException {
  // this method may generate an ArithmeticException
  // if it does, it does not need to catch it and handle it
  // the exception will be immediately "thrown" to the method
  // that called this exampleMethod() - known as the "calling method"
  // it is the calling methods responsibility to handle this error
  // or, the calling method may throw the exception to it's 
  // calling method
}

Here’s an example of a main() method calling a divide() method. The divide method “throws” any exceptions it encounters. These thrown exceptions are thrown back to the main() method which, in this case, is required to catch and handle the exception. (In this case the main() must handle it because if the main() method throws the exception it will just be thrown to the JVM which will just crash the application.

public static void main(String[] args){
  try {
    // we can see that the divide() method below "throws" an exception 
    // (potentially - based on input) so we need to use a try catch here 
    // to catch the exception that may be thrown
    int x = divide(4, 0);
  } catch(ArithmeticException exc){
    System.out.println("an exception was thrown from the divide() method.")
  }
    System.out.println("all done");
}
 

// the divide() method will throw any exception that occurs back to the method
// that called it - in this case that's the main() method above
public static int divide(int a, int b) throws ArithmeticException {
  // if "b" is zero, it will generate an ArithmeticException
  // division by zero is not allowed
  return a/b;
}

If the method divide generates an ArithmeticException exception, it will throw it back to the method that called it, in the case the main() method . If the method that called divide does not handle the exception, it will be thrown to the JVM, thus terminating the program with a disgraceful exception. Tisk tisk 🙂

Re-Throwing Exceptions in Java:

You can use “throw” and “throws” for re-throwing exceptions in Java.

When an exception is thrown and caught in a catch statement, there are two options for handling that exception. The exception can be handled inside the catch statement or it can be re-thrown. For example:

public class ThrowingExceptionsExample {
  public static void main(String[] args){t
    try {
      methodOne();
    } catch(ArrayIndexOutOfBoundsException exc){
      // this catch is actually "re-catching" the exception thrown from methodTwo
      System.out.println("exception caught");
    }
  }
 
  // methodOne will immediately throw any exceptions that occur to the main()
  public static void methodOne() throws ArrayIndexOutOfBoundsException {
    // methodTwo throws an Exception - we're not going to catch it here
    // we're going to pass it up to the main() method to handle it as needed
    methodTwo();
  }

  public static void methodTwo() throws ArrayIndexOutOfBoundsException {
    int[] nums = {1,2,3};
    try {
      int x = nums[10];
    } catch (ArrayIndexOutOfBoundsException exc){
      System.out.println("Exception caught in methodTwo");
      System.out.println("Now manually throwing exception back to methodOne");
      // the line below will "re-throw" the exception to methodOne
      throw exc;
    }
  }
}

This is a bit tricky at first. The video explanation coming next should help quite a bit.

Custom Exceptions in Java

Until now we have been dealing with Java’s built-in exception classes however, it is possible to create custom exception subclasses that extend the Exception class. Custom exceptions are incredibly powerful and useful because they can be tailored to a specific application.

To create a custom exception in Java, a subclass of Exception is declared. By extending Exception, your exception class inherits all of the methods declared in the Throwable class (because Exception is a subclass of Throwable). You can, of course, override those methods to perform a more tailored task.

Custom exceptions don’t need to implement anything to be used in a program. Adding implementation can be helpful to give users more detail when a custom exception is thrown or caught.

Below is an example of creating a very simple custom exception in Java that we can throw if/when a classroom is full.

public class ClassroomFullException extends Exception {
  @Override
  public String toString() {
    return "ClassroomFullException{ This classroom is full}";
  }
}

Now here’s an example of using that custom exception in Java:

public class Example {
  public static void main(String[] args){
    try {
      Student someStudent = new Student();
      addStudentToClassroom(someStudent);
    } catch (ClassroomFullException exc) {
      System.out.println(exc.getMessage());
      // will print: "ClassroomFullException{ This classroom is full}"
    }
  }
   
  public static void addStudentToClassroom(Student student) 
  		throws ClassroomFullException {
    if (someClassroom.size < 25) {
      someClassroom.add(someStudent)
    } else {
      // manually throw new custom exception
      throw new ClassroomFullException();
    }
  }
}

We can create as many custom exceptions to handle as many custom cases we need. Here’s another example that all of those in the United States will understand 🙂

// here we are defining a custom exception class
public class UnderAgeException extends Exception {
  @Override
  public String toString() {
    return "UnderAgeException{ Must be 21 to purchase alcohol }";
  }
}

// and here is the example class that uses our custom exception
public class Example {
  public static void main(String[] args){
    try {
      Person somePerson = new Person(19);
      Wine vino = purchaseWine(somePerson, "red");
    } catch (UnderAgeException exc) {
      System.out.println(exc.getMessage());
      // will print: "UnderAgeException{ Must be 21 to purchase alcohol }"
    }
  }
   
  public static Wine purchaseWine(Person person, String style) 
  		throws UnderAgeException {
    if (person.age >= 21) {
      return new Wine(style);
    } else {
      // manually throw new custom exception
      throw new UnderAgeException();
    }
  }
}

Summary

Exception Handling in Java is used to keep our programs from crashing when something goes wrong. It is a day-to-day skill required by all Java developers. Java exception handling consists of several main elements:

  • The Try/Catch Statement is used to “try” a block of code for potential exceptions, and “catch” any exceptions that may occur.
  • The Finally block is executed after every try/catch Java statement, whether or not the “try” block triggers the “catch” block.
  • You can “throw” exceptions from the current method to the calling method, so the calling method can then handle the exception.
  • If a method potentially throws an exception, you add the “throws” clause to the method signature to inform others that this method may “throw” an exception back at them.

It’s also helpful to know that:

  • You can use multiple “catch” statements after a single “try”.
    • When using multiple catch statements, the most specific should come first – this is the exception hierarchy in Java. You must catch subclass exceptions before (above) catching any super class exceptions.
  • You can nest Try/Catch Java Statements.
  • You can create custom exceptions in Java quite easily.

Where to go next?

If you’re interested in learning more, take a look at CodingNomads’ comprehensive online Java Programming courses. We’ve worked with hundreds of new developers to get them up to speed with Java in no time!