Skip to main content
Logo image

Problem Solving with Algorithms and Data Structures using Java: The Interactive Edition

Section B.2 Exceptions

Consider this program:
import java.util.Scanner;

public class ErrorProne {

    public static void main(String[] args) {
        int[] data = {10, 66, 47, 11};
        
        Scanner input = new Scanner(System.in);
        
        System.out.print("Enter index 0-3: ");
        int index = input.nextInt();
        
        System.out.print("Enter number to divide by: ");
        int divisor = input.nextInt();
        
        int result = data[index] / divisor;
        System.out.printf("quotient of %d and %d is %d\n",
            data[index], divisor, result);
    }
}
Listing B.2.1.
If you enter a non-number the program crashes:
Enter index 0-3: two
Exception in thread "main" java.util.InputMismatchException
	at java.base/java.util.Scanner.throwFor(Scanner.java:939)
	at java.base/java.util.Scanner.next(Scanner.java:1594)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
	at ErrorProne.main(ErrorProne.java:11)
The lines beginning with at are a stack trace. They show the chain of method calls in reverse chronological order with the file name and line number. You’ll want to look for the one that is in your program. In this case, the error was in ErrorProne.java:11, where the 11 is the line number in the source file with the nextInt call.
If you enter an index outside the array bounds, the program crashes (the output has been reformatted to fit on the line length of this page):
Enter index 0-3: 5
Enter number to divide by: 0
Exception in thread "main"
  java.lang.ArrayIndexOutOfBoundsException:
  Index 5 out of bounds for length 4
	at ErrorProne.main(ErrorProne.java:21)
And if you enter a zero as the divisor, you get yet another error:
Enter index 0-3: 2
Enter number to divide by: 0
Exception in thread "main"
  java.lang.ArithmeticException: / by zero
    at ErrorProne.main(ErrorProne.java:21)
All of these errors are called exceptions—exceptional conditions after which the program cannot continue to run. In Java, we say that the program throws an exception when it fails.
You already know how to handle these problems before they crash your program: you can use an if statement with Scanner’s hasNextInt method to make sure that the user enters an integer. You can use if statements to check that the index number is between 0 and the array’s length, and that the divisor is non-zero.

Subsection B.2.1 try and catch

In addition to using an if statement to avoid errors, Java has another general mechanism for catching exceptions before they stop your program: try and catch. Let’s enclose the code that could have an error in a try block:
try {
    System.out.print("Enter index 0-3: ");
    int index = input.nextInt();
    
    System.out.print("Enter number to divide by: ");
    int divisor = input.nextInt();
    
    int result = data[index] / divisor;
    System.out.printf("quotient of %d and %d is %d\n",
        data[index], divisor, result);
}
Listing B.2.2.
The try block is followed by a catch block that specifies the exception we want to handle and how to handle it. Let’s start with the division by zero, which generated a java.lang.ArithmeticException:
catch (ArithmeticException ex) {
    System.out.println("Number to divide by cannot be zero.");
}
Listing B.2.3.
If you recompile and run the program and enter 2 and 0 as your numbers, you’ll get the error message in the catch block. Notice that the printf statement after the division doesn’t occur—when an exception is thrown, execution imediately jumps to the catch.
If you enter five or 5 for the first input, you’ll still get the NumberFormatException or ArrayIndexOutOfBoundsException.
You may follow a try block with as many catch blocks as you want. Let’s add two more catch blocks to handle these other two errors:
catch (NumberFormatException ex) {
    System.out.println("You must enter digits for numbers.");
}
catch (ArrayIndexOutOfBoundsException ex) {
    System.out.printf("Index must be in range 0-%d\n", data.length);
}
Listing B.2.4.
The variable in parentheses after catch is local to the catch block. This means you can use the same variable name in all the catch blocks, and, by convention, most programmers name it ex. (We will put it to use later in the chapter.)
When an exception occurs, Java goes through the catch blocks in the order that they appear in your program and finds the first one that applies. In the preceding example, we could have put the catch blocks in any order. However, the order does become important once we examine the hierarchy of exceptions.

Subsection B.2.2 The Hierarchy of Exceptions

All exceptions descend from the Exception class. This list shows many of the most common exceptions you will encounter when learning Java; each category contains many other classes:
  • Exception
    • IOException
      • FileNotFoundException
    • RunTimeException
      • ArithmeticException
      • IllegalArgumentException
        • IllegalFormatException
        • InvalidParameterException
        • NumberFormatException
      • IndexOutOfBoundsException
        • ArrayIndexOutOfBoundsException
        • StringIndexOutOfBoundsException
      • NullPointerException
If you put a catch for a parent class before a catch for a child class, the parent class will catch the error. Thus, in this code fragment:
try {
    int n = 12 / 0;
}
catch (Exception ex) {
    System.out.println("Something unexpected occurred.");
}
catch (ArithmeticException ex) {
    System.out.println("You can't divide by zero.");
}
Listing B.2.5.
You will see the “Something unexpected occurred.” message. For this reason, always catch the more specific (child) exception classes before you catch the more general (parent) exception classes.
Let’s say you catch the most generic Exception possible, or one that could have many possible causes, such as FileNotFoundException. How can you give the user more information than just “something unexpected occurred”? You can use the variable that you declared in the catch clause. Here are some methods that you can use 1 
getMessage()
Returns a detailed message string
toString()
Returns a short description
printStackTrace()
This void method prints the exception and its stack trace to the standard error stream, which is your terminal window
For example, you could catch only Exception and use one of these methods to tell users what went wrong:
catch (Exception ex) {
    System.out.println("An error occurred:");
    System.out.println(ex.toString());
}
Listing B.2.6.
You can see this in action in file ErrorProneGeneralException.java in the code repository. As you can see, the error messages are not as satisfactory as those you would write yourself when handling the specific exceptions.

Subsection B.2.3 The finally Clause

Sometimes programs need to take an action whether the code in the try block succeeded or not. (For example, if you have allocated resources and want to “clean up” before exiting the program.) This is the role of the finally clause. It is executed whether the try block succeeded or an exception was caught by a catch block. In fact, it is executed even when there is a return or break in a block:
public class FinallyTest {

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        
        try {
            int result = 17 / 5;
            System.out.printf("quotient of 17 and 5 is %d\n",
               result);
            return; // exits the main() method
        }
        catch (ArithmeticException ex) {
            System.out.println("You can't divide by zero.");
        }
        finally {
            System.out.println("In the finally clause.");
        }
        System.out.println("Does not print if return succeeds");
    }
}
Listing B.2.7.
This program will print In the finally clause. even though the return prevents the last println from happening.

Subsection B.2.4 Throwing Exceptions

Before proceeding, let’s ask a philosophical question: why do these exceptions even exist? Why doesn’t the JVM simply do something reasonable when it encounters one of these situations? That’s the whole problem—what does “reasonable” mean? For dividing by zero, some programs might want to return a zero as a default answer. Other programs might want to print an error message and end the program. Still others might want to detect the attempt to divide by zero and ask for new input.
Because the definition of “reasonable” is different for every program and every programmer, it makes sense for these exceptions that happen in many different circumstances to throw the problem back to the programmer and say, “you handle this.”
If you are writing a library of Java methods for other people to use, you won’t be able to anticipate all of your users’ needs either. Your methods will also need to throw an Exception so that the people who use your methods can handle errors as they see fit.
For example, let’s say you have a Java library with a method that calculates the average of an array of double values. If somebody hands you an empty array, that’s an illegal argument, and you can write your code to throw an IllegalArgumentException:
public class ArrayStats {
    public static double average(double[] data)
        throws IllegalArgumentException {
        int n = data.length;
        if (n > 0) {
            double sum = 0.0;
            for (double value: data) {
                sum += value;
            }
            return sum / n;
        } else {
            throw new IllegalArgumentException("Empty array");
        }
    }
}
Listing B.2.8.
In line 3, specify that this method throws an IllegalArgumentException.
In line 12, when the length of the array is zero, use the keyword throw and create a new IllegalArgumentException. The argument to the constructor is the value that will be returned when someone calls the exception’s getMessage method.
Here’s an example that calls the code (without try and catch) and the resulting error, formatted to fit the page:
public class TestArrayStats {
    public static void main(String[] args) {
        double[] items = new double[0];
        double result = ArrayStats.average(items);
        System.out.println("Average is " + result);
    }
}
Listing B.2.9.
Exception in thread "main"
  java.lang.IllegalArgumentException: Empty array
        at ArrayStats.average(ArrayStats.java:12)
        at TestArrayStats.main(TestArrayStats.java:5)
If your method can throw more than one exception, you list them separated by commas in the throws clause:
public static void example(int n) throws IllegalArgumentException,
  ArithmeticException, NumberFormatException {
    // ...code
}
Listing B.2.10.

Subsection B.2.5 Checked and Unchecked Exceptions

All of the exceptions used in the preceding examples (and all exceptions that are descendants of RuntimeException) are called unchecked exceptions. Java doesn’t require you to enclose operations that cause such exceptions in a try-catch block. This is a good thing, or you’d need try-catch blocks around every division, array access, and string-to-numeric conversion.
For many operations that could throw an unchecked exception, you are better off using an if statement to avoid the error in the first place. You can, for example, use an if to check if an index is within array bounds, a divisor is non-zero, or if the user has actually entered data that can be converted to integer. This also gives you greater control over the program flow and structure. See, for example, the NormalErrorChecking.java file in the code repository.
So, why are we talking about try and catch at all? Because there are some exceptions that can’t easily be handled by an if-else. These exceptions are called checked exceptions. The Java compiler requires you to enclose operations that might throw these exceptions in a try block and provide a catch block to handle them. You are also required to list them in a throws clause if you are throwing those exceptions yourself. Foremost of these checked exceptions is the IOException, generated by I/O (Input/Output) operations. When you are working with files in Java, you will need to check this exception. Handling files is the topic of the next section of this chapter.
You have attempted of activities on this page.