In this article, we will learn about handling exceptions using try-catch blocks in Java, through examples.
Table of Contents
1. Why Do We Need to Handle Exceptions?
During the lifespan of its execution, a program might encounter an error that it cannot resolve on its own, due to its nature. For instance, let’s imagine that we are storing integers in an array and we are accessing an index that is not within the array’s capacity, like so:
private static void noExceptionHandlingExample() { int[] integerArray = new int[5]; // every element is 0 // print the array's elements System.out.println("Our initial array:"); for(int integer: integerArray) { System.out.print(integer + " "); } System.out.println(); // accessing the sixth element of an array with 5 elements // this will cause (or throw, as we say) an exception integerArray[6] = 5; System.out.println("End of noExceptionHandlingExample method"); }
The output is:
Our initial array: 0 0 0 0 0 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 5 at JavaNoExceptionHandlingExampleClass.noExceptionHandlingExample(JavaNoExceptionHandlingExampleClass.java:18) at JavaNoExceptionHandlingExampleClass.main(JavaNoExceptionHandlingExampleClass.java:3)
Let’s see what we have done here:
- Line 2: We create an array of integers with a capacity of 5 integers. Since we have just initialized it, every element has a default value of 0.
- Lines 6-8: By using an enhanced for loop, we are printing all the values of the array, just to demonstrate that everything is going well so far.
- Line 13: We try to access its sixth element, which does not exist. As a result, an
ArrayIndexOutOfBoundsException
exception is thrown and the program’s execution terminates at this point. - Line 15: The program will not execute this, because the execution has stopped, due to the exception.
As you can see, not handling an exception will force the program to stop executing. Such an event, depending on the circumstances, could have a catastrophic outcome for everyone involved.
In order to prevent that, we can use try-catch blocks in Java.
2. What Are Try-Catch Blocks in Java?
We use try-catch
blocks in Java to protect code that could throw an exception and handle that scenario accordingly. Those blocks can be accompanied by another keyword named finally
. We will see everything, one by one. We can right a try-catch
block like so:
try { // code that might cause an exception } catch (AnException e) { // code that handles this exception named "e" of type "AnException" // there can be more than one catch blocks } catch (AnotherException e) { } finally { // code that will be executed, regardless of whether an exception happens }
2.1 The try-catch Block in Java
Let’s start with the simplest form of a try-catch
block. In order to prevent a program from stopping its execution, we can do the following:
- Firstly, we enclose the code that might throw an exception within the
try
block. - Then, we define what we would like to do with the caught exception in the
catch
block.
Let’s apply these to the program above:
private static void genericExceptionHandlingExample() { int[] integerArray = new int[5]; // every element is 0 // print the array's elements System.out.println("Our initial array:"); for(int integer: integerArray) { System.out.print(integer + " "); } System.out.println(); // try to access the sixth element of the array try { integerArray[6] = 5; } catch (Exception e) { e.printStackTrace(); // prints the exception that we would get otherwise } System.out.println("End of genericExceptionHandlingExample method"); }
The output is:
Our initial array: 0 0 0 0 0 End of genericExceptionHandlingExample method java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 5 at JavaExceptionHandlingExampleClass.genericExceptionHandlingExample(JavaExceptionHandlingExampleClass.java:19) at JavaExceptionHandlingExampleClass.main(JavaExceptionHandlingExampleClass.java:3)
As you can see, a few things have changed:
- Lines 12-13: Now, we are enclosing the risky part of the execution in a
try
block. - Lines 14-15: Here, we are catching that
Exception e
and printing it. Without a try-catch block, the execution would stop here. - Line 18: The program will print this line, since the execution didn’t stop.
2.2 Using Specific Exception Classes
In Java, all exceptions are a subclass of the Exception
class. For instance, in our aforementioned example, we caught an ArrayIndexOutOfBoundsException
exception. It’s a better practice to use the more specific exception subclasses rather than the more generic Exception
class, so our program would become like this:
private static void specificExceptionHandlingExample() { int[] integerArray = new int[5]; // every element is 0 // print the array's elements System.out.println("Our initial array:"); for(int integer: integerArray) { System.out.print(integer + " "); } System.out.println(); // try to access the sixth element of the array try { integerArray[6] = 5; } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); // prints the exception that we would get otherwise } System.out.println("End of specificExceptionHandlingExample method"); }
The output is:
Our initial array: 0 0 0 0 0 End of specificExceptionHandlingExample method java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 5 at JavaExceptionHandlingExampleClass.specificExceptionHandlingExample(JavaExceptionHandlingExampleClass.java:39) at JavaExceptionHandlingExampleClass.main(JavaExceptionHandlingExampleClass.java:4)
In this case, we are catching the more specific ArrayIndexOutOfBoundsException
. Apart from the last System.out.println
statement, there is no other difference compared to the example in section 2.1.
2.3 Catching Multiple Exceptions
We can use more catch multiple exceptions in one try
block by simply adding another catch block after the previous one. From Java 7 onwards, a catch block can have multiple exceptions, separated by the |
operator.
try { } catch (Exception1 e | Exception 2) { }
Note that we have to declare the catch
blocks from the most specific to the most generic, so an Exception
catch block will always be the last. If we were to place a more generic exception and then a more specific one, we would get a compilation error indicating that the generic exception has already handled the exception.
Also, if an exception is caused in the try
block any possible exceptions in the same try
block will not be checked, because once the execution leaves the try
block, it won’t go back to it.
So, in order to demonstrate the versatility of the multiple catch
blocks we have created the following example:
private static void multipleExceptionHandlingExample() { int[] integerArray = new int[5]; // every element is 0.0 // we execute an assignment at a random index with a random number 6 times, catching any exception that happens along the way for(int i = 0; i < 6; i++) { System.out.println("Attempt " + (i + 1)); int randomNumber = (int) (15 + Math.random() * 15); // numbers in [15,30] int randomIndex = (int) (0 + Math.random() * 10); // indices in [0,10] boolean divideByZero = Math.random() > 0.5 ? true : false; try { if(divideByZero) { integerArray[randomIndex] = randomNumber / 0; } else { integerArray[randomIndex] = randomNumber; } System.out.println("Successfully assigned integerArray[" + randomIndex + "] = " + randomNumber); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Inside ArrayIndexOutOfBoundsException, because we tried to access the index " + randomIndex); } catch (ArithmeticException e) { System.out.println("Inside ArithmeticException, because we tried to divide zero with " + randomNumber); } System.out.println(); } }
Since this method uses Math.random(), your output will be different than the provided output. Nevertheless, the output is:
Attempt 1 Successfully assigned integerArray[4] = 17 Attempt 2 Inside ArrayIndexOutOfBoundsException, because we tried to access the index 9 Attempt 3 Inside ArithmeticException, because we tried to divide zero with 15 Attempt 4 Inside ArrayIndexOutOfBoundsException, because we tried to access the index 7 Attempt 5 Successfully assigned integerArray[4] = 21 Attempt 6 Inside ArithmeticException, because we tried to divide zero with 24
We have done quite a few things, so let’s elaborate:
- Line 2: We define an array of integers. We will use this array’s element to assign new integers to them.
- Line 5: Here, we begin executing the rest of the method 10 times. In each attempt, Math.random() produces different results, so we can see different outcomes.
- Line 8: An integer is generated within the
[15, 30]
range. We will attempt to assign this number to an element of the array. - Line 9: Anothe integer is generated within the
[0, 10]
range. This number will tell us which element of the array to access. Note that only 0-4 are valid indices. For the numbers 5-10, we will get anArrayIndexOutOfBoundsException
. - Line 10: This boolean is set according to another
Math.random()
call and will decide wether we will divide by zero or not. This will cause anArithmeticException
if we do divide with zero. - Line 14: This assignment will cause the
ArithmeticException
. The program soesn’t check the index number since the execution stops at the division. - Line 16: On the other hand, this assignment might or might not cause
ArrayIndexOutOfBoundsException
, which depends onrandomIndex
.
3. The finally block
When we want to ensure that some code will get executed no matter what the outcome of try-catch
is, then we can use a finally
block.
private static void finallyBlockExample(int firstNumber, int secondNumber) { System.out.println("Trying to do the division: " + firstNumber + " / " + secondNumber); try { int number = firstNumber / secondNumber; System.out.println(number); } catch (ArithmeticException e) { System.out.println("An ArithmeticException was caused"); } finally { System.out.println("Inside finally block"); } System.out.println("End of finallyBlockExample method\n"); }
By calling the above method with finallyBlockExample(6, 2)
and finallyBlockExample(3, 0)
, we get the following output:
Trying to do the division: 6 / 2 3 Inside finally block End of finallyBlockExample method Trying to do the division: 3 / 0 An ArithmeticException was caused Inside finally block End of finallyBlockExample method
As you can see, the finally
block gets executed in both cases and the execution didn’t stop, since the last System.out.println
was executed. Someone could argue that, in this case, we could simply omit the finally
block and the output would be the same. Here, indeed the finally
didn’t provide us with much, but we could see its true power, by slightly altering the method above to catch another exception instead of ArithmeticException
as such:
private static void trueFinallyBlockExample(int firstNumber, int secondNumber) { System.out.println("Trying to do the division: " + firstNumber + " / " + secondNumber); try { int number = firstNumber / secondNumber; System.out.println(number); } catch (IndexOutOfBoundsException e) { System.out.println("An IndexOutOfBoundsException was caused"); } finally { System.out.println("Inside finally block"); } System.out.println("End of trueFinallyBlockExample method\n"); }
By calling the above method with trueFinallyBlockExample(6, 2)
and trueFinallyBlockExample(3, 0)
, we get the following output:
Trying to do the division: 6 / 2 3 Inside finally block End of trueFinallyBlockExample method Trying to do the division: 3 / 0 Inside finally block Exception in thread "main" java.lang.ArithmeticException: / by zero at JavaExceptionHandlingExampleClass.trueFinallyBlockExample(JavaExceptionHandlingExampleClass.java:108) at JavaExceptionHandlingExampleClass.main(JavaExceptionHandlingExampleClass.java:17)
In this case, we try catching an exception that will never happen, IndexOutOfBoundsException
. As such, the execution of the program stops, since we do not catch it, so the last System.out.println
isn’t executed (it’s as if we didn’t have a try-catch
block). Nonetheless, the finally
block is still executed in both cases, despite the halt in execution.
4. The throw Keyword
Sometimes, it makes sense for us to create an exception ourselves and let another method handle it accordingly. We can do this by using the throw
keyword.
In the example below, we are throwing an IllegalArgumentException
if the argument of the printPositiveNumber(int number)
method is not a positive number.
public class JavaThrowingExceptionsExampleClass { private static void printPositiveNumber(int number) { if(number <= 0) { throw new IllegalArgumentException("Only positive numbers are allowed, " + number + " is not positive."); } else { System.out.println("All good, " + number + " is positive."); } } public static void main(String[] args) { try { printPositiveNumber(6); System.out.println(); printPositiveNumber(-4); } catch (IllegalArgumentException e) { System.err.println(e); } } }
The output is:
All good, 6 is positive. java.lang.IllegalArgumentException: Only positive numbers are allowed, -4 is not positive.
Let’s explain a few things:
- Line 14: we
throw
anIllegalArgumentException
if the argument is zero or less. Inside the brackets we place a custom message that we would like to include when that exception is thrown. - Line 18:
System.err.println()
prints the output in red color. It’s mostly used to indicate that the print was made from catching an exception. Other than that, it’s the same as the typicalSystem.out.println()
.
5. Conclusion
By now, you should have a solid understanding of try-catch blocks in Java. You can find the source code on our GitHub page.