What is Exception Handling in Java?
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.
Table of Contents:
- Java Exception Handling Keywords
- Uncaught Java Exceptions
- Exception Hierarchy in Java
- Built-in Java Exceptions
- Multiple Catch Statements
- Subclass Exceptions
- Nested Try Blocks
- How to throw an exception in Java
- Adding “throws” To The Method Signature
- Re-Throwing Exceptions in Java
- Custom Exceptions in Java
- Where to go next?
Java Exception Handling Keywords
In this section we’ll briefly introduce the primary concepts before diving a bit deeper later in this tutorial.
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”.
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.
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.
“throw” is a keyword 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.
“throws” is a keyword used in method signatures to indicate that this method has the potential to throw one or more given exceptions. For example:
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:
Stack Trace: Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 7 at NotHandled.main(NotHandled.java:9)
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:
Arithmetic error (divide-by-zero)
Array index is out-of-bounds
Illegal argument used to call a method
Requested operation not compatible with current thread state
Some type of index is out-of-bounds
Invalid use of a null reference
Invalid conversion of a string to a numeric format
Attempt to index outside the bounds of a string
Type not found
Learn beginner-to-advanced Java skills:
- Training 1: Intro to Java Basic Concepts
- Training 2: Object Oriented Programming
- Training 3: Data Structures
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.
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.
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.
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.
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 green box with the “play” button.
Do you see the exception thrown?
Does “Program Complete” print?
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.
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:
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.
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:
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.
Now here’s an example of using that custom exception in Java:
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 🙂
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 course over on our online learning platform. We’ve worked with hundreds of new developers to get them up to speed with Java in no time!
Let’s be friends