5. Exception Handling
Programming Project 2022/23

5.1. Introduction

Learning goals

In this module, you will learn about

  1. exception occurrences,
  2. Throwable, Exception, RuntimeException, and Error, expressions
  3. handling exceptions using try-catch blocks,
  4. declaring exceptions, and
  5. creating custom exceptions.

Introduction

Consider the following program.

import java.util.Scanner;

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

      System.out.print("Please enter an integer numerator: ");
      int numerator = scanner.nextInt();

      System.out.print("Please enter an integer denominator: ");
      int denominator = scanner.nextInt();

      int quotient = quotient(numerator, denominator);
      System.out.printf("%nResult: %d / %d = %d%n", numerator, denominator, quotient);
   }

   public static int quotient(int numerator, int denominator){
      return numerator / denominator;
   }
}

What will happen if we input 0 as the denominator?

What if you input a String instead of an int?

Our program will throw exceptions!

What is an exception?

An exception is an event, which occurs during the execution of a program and disrupts the normal flow of the program's instructions..

Some examples include

  • division by zero,
  • array access out of bounds, and
  • attempting to access a field via a null pointer.

An exception is an object that contains

  • information about the error, including its type,
  • the state of the program when it occurred, and
  • custom information.

The term exception is shorthand for the phrase "exceptional event".

Source

What happens when an exception occurs?

  1. The method stops executing.
  2. The method creates an exception object.
  3. The methods passes the exception object to the runtime system.
  4. The runtime system searches the call stack for an exception handler.
  5. If the runtime system finds an appropriate exception handler, the exception is passed to it.
  6. The exception handler starts to execute.

If no exception handler is found, the program prints the call stack to the console and terminates.


exception-process

We often use the following expressions.

  • to throw an exception refers to creating an exception object and passing it to the runtime system.
  • to catch an exception refers to passing an exception by the runtime system to a piece of code that can handle the exception.

Call Stack

The call stack is the ordered list of methods that had been called to get to the method where the exception occurred.

Here is a couple of console outputs that show us the call stack from exceptions thrown by our program above.

Please enter an integer numerator: 100
Please enter an integer denominator: 0
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at DivideByZeroNoExceptionHandling.quotient(DivideByZeroNoExceptionHandling.java:7)
	at DivideByZeroNoExceptionHandling.main(DivideByZeroNoExceptionHandling.java:19)
Please enter an integer numerator: a
Exception in thread "main" java.util.InputMismatchException
	at java.util.Scanner.throwFor(Scanner.java:864)
	at java.util.Scanner.next(Scanner.java:1485)
	at java.util.Scanner.nextInt(Scanner.java:2117)
	at java.util.Scanner.nextInt(Scanner.java:2076)
	at DivideByZeroNoExceptionHandling.main(DivideByZeroNoExceptionHandling.java:14)

We can get the following information from the call stack.

  • The thread in which the exception occurred.
  • The method which threw the exception.
  • The exact line which threw the exception.
  • The ordered list of method calls at the moment the exception was thrown.

Tip: To understand what broke your code and discover how to fix it, start by looking at the first method you wrote in the call stack.

Exception handlers

An exception handler is a block of code that can handle exceptions of one or more types.

The basic structure of an exception handler looks like this:

try {
  // The code we want to secure
} catch (ExceptionType exception) {
  // The code we want to execute if an exception of type ExceptionType is thrown
} finally {
  // The code we want to execute regardless if an exception has been thrown or not
}

It must have exactly one try block.

It may have zero or more catch blocks.

It may have at most one finally block.

However, it must either have a catch or a finally block.

Here is how we can add exception handlers to our previous program.

import java.util.InputMismatchException;
import java.util.Scanner;

public class DivideByZeroExceptionHandling {
  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    boolean continueLoop = true; // determines if more input is needed

    while (continueLoop) {
      try { // read two numbers and calculate quotient
        System.out.print("Please enter an integer numerator: ");
        int numerator = scanner.nextInt();

        System.out.print("Please enter an integer denominator: ");
        int denominator = scanner.nextInt();

        int quotient = quotient(numerator, denominator);
        System.out.printf("%nResult: %d / %d = %d%n", numerator, denominator, quotient);

        continueLoop = false;
      } catch (InputMismatchException e) {
        e.printStackTrace();
        scanner.nextLine(); // discard input so we can try again
        System.out.printf("You must enter integers. Please try again.%n");
      } catch (ArithmeticException e) {
        e.printStackTrace();
        System.out.printf("Cannot divide by 0. Please try again.%n");
      }
    }
  }

  public static int quotient(int numerator, int denominator) {
    return numerator / denominator;
  }
}

The try block

The first step in constructing an exception handler is to enclose the code that might throw an exception within a try block.

try {
  System.out.print("Please enter an integer numerator: ");
  int numerator = scanner.nextInt();
  // ...
}

If an exception occurs within the try block, it will be first passed to its catch block and then to its finally block.

Source

The catch block

A catch block is preceded by a try block or another catch block.

Here is an example.

catch (ArithmeticException e) {
  // ...
}

A catch block matches a thrown exception if it is of the type defined in block or is a subtype of it. This means that we can write a catch block that matches all possible exceptions by making it as general as possible.

catch (Exception e) {
  // ...
}

Alternatively, we can declare multiple catch blocks to handle exception types differently.

catch (InputMismatchException e) {
  // ...
}
catch (ArithmeticException e) {
  // ...
}

Another option is catching several specific exception types in a single catch block.

catch (ArithmeticException | InputMismatchException e) {
  // ...
}

Here is how our previous example would look like.

import java.util.InputMismatchException;
import java.util.Scanner;

public class DivideByZeroExceptionHandlingSingle {
  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    boolean continueLoop = true; // determines if more input is needed

    while (continueLoop) {
      try { // read two numbers and calculate quotient
        System.out.print("Please enter an integer numerator: ");
        int numerator = scanner.nextInt();

        System.out.print("Please enter an integer denominator: ");
        int denominator = scanner.nextInt();

        int quotient = quotient(numerator, denominator);
        System.out.printf("%nResult: %d / %d = %d%n", numerator, denominator, quotient);

        continueLoop = false;
      } catch (ArithmeticException | InputMismatchException e) {
        e.printStackTrace();
        scanner.nextLine(); // discard input so we can try again
        System.out.printf("Either an ArithmeticException or an InputMismatchException was thrown. Please try again.%n");
      }
    }
  }

  public static int quotient(int numerator, int denominator) {
    return numerator / denominator;
  }
}

Source

Exercise 1: Using try-catch

  1. Write a program that throws an ArrayIndexOutOfBoundsException.
  2. Change the previous program so that it will catch the thrown exception.

You can find the solution to this exercise here.

The finally block

The finally block always executes after a try block is executed, which ensures that it is executed even if an unexpected exception occurs.

The finally block helps to makes sure that a cleanup code segment is always executed, and not accidentally bypassed by a return, continue, or break. Thus, using a finally block is always a good practice, even when no exceptions are anticipated.

The finally block may, however, not execute if the JVM exits while the try or catch code is being executed.

Let us observe the how the following code is executed.

public class FinallyExample {
  public static void main(String[] args) {
    try {
      throwException();
      System.out.println("Line after throwException() call");
    } catch (Exception e) {
      System.out.println("4 Catch block in main()");
    }
    doesNotThrowException();
  }

  public static void throwException() throws Exception {
    try {
      System.out.println("1 Try block in throwException()");
      throw new Exception();
    } catch (Exception exception) {
      System.out.println("2 Catch block in throwException()");
      throw exception;
    } finally {
      System.out.println("3 Finally block in throwException()");
    }
  }

  public static void doesNotThrowException() {
    try {
      System.out.println("5 Try block in doesNotThrowException()");
    } catch (Exception exception) {
      System.out.println("6 Catch block in doesNotThrowException()");
    } finally {
      System.out.println("7 Finally block in doesNotThrowException()");
    }
    System.out.println("8 End of doesNotThrowException()");
  }
}

Now, let us add a finally block to our previous example.

import java.util.InputMismatchException;
import java.util.Scanner;

public class DivideByZeroExceptionHandlingFinally {
  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    boolean continueLoop = true; // determines if more input is needed

    while (continueLoop) {
      Integer numerator, denominator, quotient = null;

      try { // read two numbers and calculate quotient
        System.out.print("Please enter an integer numerator: ");
        numerator = scanner.nextInt();

        System.out.print("Please enter an integer denominator: ");
        denominator = scanner.nextInt();

        quotient = quotient(numerator, denominator);
        System.out.printf("%nResult: %d / %d = %d%n", numerator, denominator, quotient);

        continueLoop = false;
      } catch (InputMismatchException e) {
        scanner.nextLine(); // discard input so we can try again
        e.printStackTrace();
        System.out.printf("You must enter integers. Please try again.%n");
      } catch (ArithmeticException e) {
        e.printStackTrace();
        System.out.printf("Cannot divide by 0. Please try again.%n");
      } finally {
        if (quotient != null)
          System.out.printf("OK: Successful division!%n");
        else
          System.out.printf("ERROR: Unsuccessful division!%n");
      }
    }
  }

  public static int quotient(int numerator, int denominator) {
    return numerator / denominator;
  }
}

Source

Do we really need the finally block?

An actual question from Stackoverflow.

There are 3 permutations of a try...catch...finally block in java.

  1. try...catch
  2. try...catch...finally
  3. try...finally

Once the finally block is executed, control goes to the next line after the finally block. If I remove the finally block and move all its statements to the line after the try...catch block, would that have the same effect as having them in the finally block?

Some appropriate answers to this questions are:

  1. Exceptions are for actual exceptional situations.
    • Your code will not necessarily continue its execution after an exception is thrown.
    • We do not want the clean execution of our program to be interrupted by an exception.
  2. The finally block will always be executed. This ensures that the finally block is executed even if an unexpected exception occurs. The finally block allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break.
    public void myFunc() {
      double p = 1.0D;
      String str = "bla";
      try {
        p = Double.valueOf(str);
      }
      catch (Exception ex){
        System.out.println("Exception Happened");
        return;  //return statement here!!!
      } finally{
        System.out.println("Finally");
      }
      System.out.println("After finally");
    }
    Exception Happened 
    Finally

Exercise 2: Using try-catch-finally

  • Write a main method with two try-catch-finally blocks.
    • In the first try block, throw an exception.
    • In the second try block, do not throw an exception.
    • In each try, catch, and finally block print out a message in the console.

You can find the solution to this exercise here.

Try-with-resources

The code below uses the try-with-resources statement.

import java.io.FileWriter;
import java.io.IOException;

public class TryWithResources {

  public static void main(String[] args) {
    String fileName = "bohemian-rhapsody.txt";
    
    try (FileWriter writer = new FileWriter(fileName)) {
      writer.write("Is this the real life?\n");
      writer.write("Is this just fantasy?\n");
      writer.write("Caught in a landslide\n");
      writer.write("No escape from reality\n");
    } catch(IOException e){
      System.out.println("Could not write to file! :(");
    }
  }

}

Such an expression can often be used instead of a finally block.

However, the opened resource must implement the AutoCloseable interface.