3.5. Handling Exceptions (Try-Catch)

We will continue with the same code example from the last section:

Original Example
public class Exception {
   public static void main(String[] args) {
      exception();
   } // main

   public static void exception() {
      String s = null;

      if (s.length() > 1) {
         System.out.println("string length > 1");
      } // if
   } // exception
} // Exception

To handle the exception in the example above, you need to make use of a special control flow syntax known as a try block or try-catch block. With this syntax, you place code that can throw an exception into the try block, then place code for how you want to deal with the exception in the catch block. These two go together, which is why we often refer to it as a try-catch block. During execution, the try-catch block works as follows:

Try-Catch Semantics

  1. Each line in the try block is executed until an exception object is thrown. If an exception is not thrown, the try block completes normally and the code in the catch block(s) is not executed.

  2. If an exception is thrown from within the try block, the JVM redirects the flow of control into the appropriate matching catch block. In other words, the code in the try block stops executing and the program jumps to the catch block associated with the type of exception that occurred.

    Note

    A try block can have multiple associated catch blocks. The only catch block that will execute is the one that is associated with the type of exception that is thrown.

  3. After the catch block is executed, the flow of control is redirected immediately after the entire try-catch construct.

Here is an example (Notice that we specify NullPointerException in the catch block:

try {
   if (s.length() > 1) {
      // Prints only when s != null
      System.out.println("string length > 1");
   } // if
} catch (NullPointerException npe) {
   // Prints only when s == null
   System.out.println("a NullPointerException was thrown!");
} // try

// Always prints
System.out.println("I will print regardless of the value of s.");

This try-catch block differentiates itself from the previous examples in two ways:

  1. we did not perform any condition checking for the exceptional situation–in this case, we did not compare the value of s to null; and

  2. the exception is still reported, however, it’s done so using code that we wrote (and, therefore, can customize) and using code that does not necessarily cause the program to abruptly terminate.

Here is a video with a larger code example demonstrating the benefits of handling exceptions instead of trying to avoid them:

https://www.youtube.com/watch?v=TETdh48t4YM

IMAGE ALT TEXT

Learn by Experimenting!

Using the try-catch semantics as a guide, try to predict the output from each of the programs below. Then, quickly test your answers by compiling/running the programs on Odin.

  1. What is the full the output of the following program?

     1public class DivisionExample {
     2   public static void main(String[] args) {
     3      int a = 10;
     4      int b = 2;
     5
     6      try {
     7         int result = a / b;
     8         System.out.println("Result: " + result);
     9      } catch (ArithmeticException e) {
    10         System.out.println("Cannot divide by zero!");
    11      } // try
    12   } // main
    13} // DivisionExample
    
  2. What would be the output of the program above if we changed variable b to have a value of 0 instead of 2?

  3. What is the full output of the following program?

     1public class MultipleCatchExample {
     2   public static void main(String[] args) {
     3      try {
     4         int[] array = {1, 2, 3};   // 1
     5         int a = 10, b = 0;         // 2
     6         String str = null;         // 3
     7
     8         int result = a / b;
     9
    10         System.out.println("Array element: " + array[5]);
    11         System.out.println("String length: " + str.length());
    12
    13      } catch (ArithmeticException ae) {
    14         System.out.println("Cannot divide by zero.");
    15      } catch (ArrayIndexOutOfBoundsException aioobe) {
    16         System.out.println("Invalid array index.");
    17      } catch (NullPointerException e) {
    18         System.out.println("Cannot call a method on a null reference.");
    19      } // try
    20
    21      System.out.println("Got to the end!");
    22   } // main
    23} // MultipleCatchExample
    
  4. Change the values assigned to the variables declared on the lines marked 1, 2, and 3 so that no exceptions are thrown and the program only prints output from the two print statements in the try block along with “Got to the end!” from the final print statement.

  5. It is not possible to make the program print both “Invalid array index.” and “Cannot divide by zero.” only by changing the values assigned in the lines marked 1, 2, and 3. Why is this not possible?

Remember to check your answers by running the code on Odin. We encourage you to explore other questions that may arise while working through these. As always, post any questions you have on the course Piazza page. Posting questions/answers to questions found in the reading is encouraged!

When handling exceptions, you should be careful that the exceptions you are catching match the exception being thrown.

Important

Double-check that you are catching the appropriate exception(s). Always carefully consider the code within the try block and which exceptions are possible. Then, catch each one separately.

For example, try to predict the output of the code below. Are we handling all possible exceptions? Once you have come up with an answer, check yourself with the solution or run the code on Odin.

 1public class MultipleCatchExample {
 2    public static void main(String[] args) {
 3
 4        String[] array = {"Hello", "1302", "Students"};
 5
 6        try {
 7            MultipleCatchExample.printIntegers(array);
 8        } catch (NullPointerException e) {
 9            System.out.println("Invalid input.");
10        } // try
11
12        System.out.println("Got to the end!");
13    } // main
14
15    /**
16     * Prints the first five characters of each string in the
17     * specified array.
18     *
19     * @param strs the specified array
20     * @throws IndexOutOfBoundsException if the string has fewer than
21     * three characters.
22     * @throws NullPointerException if the specified array is {@code null}.
23     */
24    public static void printIntegers(String[] strs) {
25
26        for (int i = 0; i < strs.length; i++) {
27            System.out.println(strs[i].substring(0, 5));
28        } // for
29
30    } // printIntegers
31} // MultipleCatchExample
Solution

The output is:

Hello
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: begin 0, end 5, length 4
    at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4604)
    at java.base/java.lang.String.substring(String.java:2707)
    at MultipleCatchExample.printIntegers(MultipleCatchExample.java:27)
    at MultipleCatchExample.main(MultipleCatchExample.java:7)

Notice how the first output, “Hello”, comes out fine. However, the second causes an exception. The exception that is thrown, StringIndexOutOfBoundsException, is never caught by our program. We only handle the case when the array is null.