Learn Java

Java Exceptions

Some errors in your code do not prevent the program from running and, in this case, the program will only crash while trying to execute a "broken" line: a line with an error called an exception. Thus, exceptions are the errors detected during the program execution (at runtime), whereas syntax errors are those detected during program compilation into byte-code. An exception interrupts the normal flow of a program.

Let's consider some types of exceptions, the conditions where exceptions occur, and how to avoid them.

ArithmeticException

Suppose you are writing a program that reads two integers from the standard input and then outputs the result of the integer division of the first number by the second one. Take a look at the code below:

import java.util.Scanner;

public class ArithmeticExceptionDemo {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int a = scanner.nextInt();
        int b = scanner.nextInt();

        System.out.println(a / b); // an exception is possible here!
        System.out.println("finished");
    }
}

If the user passes 9 and 3 as the input values, the program outputs 3 as well as the "finished" string. But if the second number is 0, the program throws an exception because of division by zero is undefined, and the "finished" string is not printed at all.

Exception in thread "main" java.lang.ArithmeticException: / by zero
  at ArithmeticExceptionDemo.main(ArithmeticExceptionDemo.java:11)

All the code before the exception is executed properly, and everything after is not.

The displayed message shows the cause of the exception — ("/ by zero"), the file, and the line number where it has occurred (ArithmeticExceptionDemo.java:11o). The provided information is useful for developers, but it is not very meaningful for the end-users of the program.

To avoid the exception, we can check the value before the division, and, if the value is zero, print a message. Here is another version of the program: If the second number is zero, the program will print the "Division by zero!" string.

import java.util.Scanner;

public class ArithmeticExceptionDemo {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int a = scanner.nextInt();
        int b = scanner.nextInt();

        if (b == 0) {
            System.out.println("Division by zero!");
        } else {
            System.out.println(a / b);
        }
        System.out.println("finished");
    }
}

Look at an input example where we try to input zero as a divider:

3 0

The result is:

Division by zero!
finished

As you can see, the new version of the program does not throw an exception and always successfully finishes. Additionally, it prints a user-friendly message instead of the standard message.

NumberFormatException

Another situation to consider is when you are trying to convert a string into an integer number: If the string has an unsuitable format, the code will throw an exception.

The following program reads a line from the standard input and then outputs the number that follows it.

import java.util.Scanner;

public class NumberFormatExceptionDemo {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();

        int number = Integer.parseInt(input); // an exception is possible here!
        System.out.println(number + 1);
    }
}

It works pretty well if the input line is a correct integer number, but if the input is not correct (e.g. "121a"), the program will fail:

Exception in thread "main" java.lang.NumberFormatException: For input string: "121a"
  at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
  at java.base/java.lang.Integer.parseInt(Integer.java:652)
  at java.base/java.lang.Integer.parseInt(Integer.java:770)
  at NumberFormatExceptionDemo.main(NumberFormatExceptionDemo.java:9)

This message displays the type of exception (NumberFormatException) and the passed input string. The place where the exception occurred is shown in the last line of the message:

  • the filename is NumberFormatExceptionDemo.java;
  • the main method;
  • at line 9.

All the previous lines of the message show the positions inside the parseInt method which is part of the standard library.

To avoid this exception, it is possible to check the input string by using a regular expression. In case the input is not correct, we can output a warning message:

import java.util.Scanner;

public class NumberFormatExceptionDemo {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();

        if (input.matches("\\d+")) { // it checks if the input line contains only digits
            int number = Integer.parseInt(input);
            System.out.println(number + 1);
        } else {
            System.out.println("Incorrect number: " + input);
        }
    }
}

If the input line is "121a", the program will not stop, and it will print the message:

Incorrect number: 121a

Hierarchy of exceptions

The following picture illustrates the simplified hierarchy of exceptions:

Hierarchy of exceptions

The base class for all exceptions is java.lang.Throwable. This class provides a set of common methods for all exceptions:

  • String getMessage() returns the detailed string message of this exception object;
  • Throwable getCause() returns the cause of this exception or null if the cause is nonexistent or unknown;
  • printStackTrace() prints the stack trace on the standard error stream.

We will return to the methods and constructors of this class in the following topics.

The Throwable class has two direct subclasses: java.lang.Error and java.lang.Exception.

  • subclasses of the Error class represent low-level exceptions in the JVM, for example: OutOfMemoryErrorStackOverflowError;
  • subclasses of the Exception class deal with exceptional events inside applications, such as: RuntimeExceptionIOException;
  • the RuntimeException class is a rather special subclass of Exception. It represents so-called unchecked exceptions, including: ArithmeticExceptionNumberFormatExceptionNullPointerException.

While developing an application, you will normally process objects of the Exception class and its subclasses. We won't discuss Error and its subclasses here.

The four basic exception classes (ThrowableExceptionRuntimeException and Error) are located in the java.lang package. They do not need to be imported. Yet their subclasses might be placed in different packages.

Checked and unchecked exceptions

All exceptions can be divided into two groups: checked and unchecked. They are functionally equivalent but there is a difference from the compiler's point of view.

1. Checked exceptions are represented by the Exception class, excluding the RuntimeException subclass. The compiler checks whether the programmer expects the occurrence of such exceptions in a program or not.

If a method throws a checked exception, this must be marked in the declaration using the special throws keyword. Otherwise, the program will not compile.

Let's take a look at the example. We use the Scanner class, which you are already familiar with, as a means to read from standard input, to read from a file:

public static String readLineFromFile() throws FileNotFoundException {
    Scanner scanner = new Scanner(new File("file.txt")); // throws java.io.FileNotFoundException
    return scanner.nextLine();
}

FileNotFoundException is a standard checked exception. This constructor of Scanner declares that it may throw the FileNotFoundException exception if the specified file does not exist. To ensure that this method can be compiled successfully, we must include the throws keyword in the method declaration to indicate that the method may throw the FileNotFoundException exception. As a result, the caller of this method will need to decide whether to handle the exception internally or throw it further to its caller method.

2. Unchecked exceptions are represented by the RuntimeException class and all its subclasses. The compiler does not check whether the programmer expects the occurrence of such exceptions in a program.

Here is a method that throws a NumberFormatException when the input string has an invalid format (for example: abc).

public static Long convertStringToLong(String str) {
    return Long.parseLong(str); // It may throw a NumberFormatException
}

The example in this block of code always successfully compiles without the throws keyword in the declaration.

Runtime exceptions may occur anywhere in a program. The compiler doesn't require that you specify runtime exceptions in declarations. Adding them to each method's declaration would reduce the clarity of a program.

The Error class and its subclasses are also considered as unchecked exceptions. However, they form a separate class.

Conclusion

Exceptions do not prevent a program from being compiled and run, but there is another issue - the program crashes as soon as the line with an exception is being executed.

You can use control statements to avoid some kinds of exceptions (like ArithmeticException or NumberFormatException) in your programs.

All exceptions are represented by the Throwable class , which has two subclasses: Exception and Error. There are also two exception types: checked and unchecked.

Unchecked exceptions are not validated by the compiler, so you don't have to handle them. They are represented by the RuntimeException subclass of the Exception class. Errors from the Error class are also considered unchecked.

Checked exceptions have to be handled and indicated explicitly. They are located in all the other subclasses of Exception.

Create a free account to access the full topic

“It has all the necessary theory, lots of practice, and projects of different levels. I haven't skipped any of the 3000+ coding exercises.”
Andrei Maftei
Hyperskill Graduate