Try-Catch in Java

by Dimitris Tasios
44 views

In this article, we will learn about handling exceptions using try-catch blocks in Java, through examples.

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:

  1. Firstly, we enclose the code that might throw an exception within the try block.
  2. 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 an ArrayIndexOutOfBoundsException.
  • 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 an ArithmeticException 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 on randomIndex.

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 an IllegalArgumentException 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 typical System.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.

6. Sources

[1]: The try Block – Oracle

[2]: The catch Blocks – Oracle

[3]: The finally Block – Oracle

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.

Related Posts