Home CPSC 220

Exceptions

 

Handling Errors

When Java programs encounter errors, they throw exceptions. You can see this if you try one of the following snippets of code:

By default, a exception will completely stop your program and print an error message to the user.

Today we will look at how to handle exceptions more gracefully.


 

Exceptions

An exception is a programming construct specifically for handling errors.

There are three parts of an exception:


 

Throwing an Exception

Exceptions are created with the "throw" keyword in Java. For example, the following program throws an exception to avoid a divide by zero


public class Slopes {

    public static double slope(int x0, int y0, int x1, int y1) {
        // avoid dividing by zero
        if (x1 == x0) {
            throw new ArithmeticException();
        }

        return (double) (y1 - y0) / (double) (x1 - x0);
    }

    public static void main(String args[]) {

        // OK
        double slope1 = slope(3, 4, 5, 6);
        System.out.println("Slope = " + slope1);

        // Exception!
        double slope2 = slope(3, 4, 3, 6);
        System.out.println("Slope = " + slope2);
    }
}

The slope function throws an exception rather than try to divide by zero.


 

Catching Exceptions

In the program above, the exception halts the program. If we want the program to recover from the exception, we must catch it.

To catch an exception, we use a try/catch block. The form of this is as follows:


try {
   // code that can cause an exception
} catch(type value) {
   // handle any exception here
}

Whenever an exception is thrown, the program will search for a catch block that catches that type of exception. When it finds one, that code is executed - the code that caused the exception is abandoned.

The goal of the catch block is to try to handle the problem as best as we can.

In this example, we handle the problem of a vertical slope by printing a special message that the line is vertical.


public class Slope2 {

    public static double slope(int x0, int y0, int x1, int y1) {
        // avoid dividing by zero
        if (x1 == x0) {
            throw new ArithmeticException();
        }

        return (double) (y1 - y0) / (double) (x1 - x0);
    }

    public static void main(String args[]) {

        // try to find slope 1
        try {
            double slope1 = slope(3, 4, 5, 6);
            System.out.println("Slope = " + slope1);
        } catch (ArithmeticException e) {
            System.out.println("The line 1 is vertical!");
        }

        // try to find slope 2
        try {

            double slope2 = slope(3, 4, 3, 6);
            System.out.println("Slope = " + slope2);
        } catch (ArithmeticException e) {
            System.out.println("The line 2 is vertical!");
        }
    }
}

Why not have the Line class print that message inside of the slope method?


 

Ignoring Some Errors

In one of our maze programs, we included code to ensure we never accessed an array out of bounds:


    // function to determine if the runner can go to a possible
    // location in the maze
    public static boolean canGo(char maze[][], int possible[]) {
        // if out of bounds, you cannot go there!
        if (possible[0] < 0) {
            return false;
        }
        if (possible[1] < 0) {
            return false;
        }
        if (possible[0] >= maze.length) {
            return false;
        }
        if (possible[1] >= maze[0].length) {
            return false;
        }
        
        // if it's a wall you cannot go there!
        if (maze[possible[0]][possible[1]] == '#') {
            return false;
        }

        // otherwise, you can!
        return true;
    }

Instead, we can actually just go ahead and try the indices, and have the catch statement indicate the space is out of bounds:


    // function to determine if the runner can go to a possible
    // location in the maze
    public static boolean canGo(char maze[][], int possible[]) {
        
        // try to index the maze
        try {
            if (maze[possible[0]][possible[1]] == '#') {
                return false;
            }

            // otherwise, you can!
            return true;
        } catch (ArrayIndexOutOfBoundsException e) {
            // if the indices were out of bounds, we can't go there
            return false;
        }
    }

This pattern is sometimes used in Java to make programs shorter.


 

Input Validation

Many parts of the Java library use exceptions to communicate errors. For example, the Scanner class throws InputMismatchException when the input doesn't match what's expected.

If we want to validate input types, we can catch and deal with this exception:


import java.util.*;

public class InputValid {
  public static void main(String args[]) {
    // create a Scanner object
    Scanner in = new Scanner(System.in);
    
    // prompt for input
    System.out.println("Enter a number: ");

    // read in an int
    int number = 0;
    boolean done = false;
    while(!done) {
      // try to get the number
      try {
        number = in.nextInt();
        done = true;
        // catch bad input
      } catch(InputMismatchException e) {
        // scold them and clear the garbage they entered
        System.out.println("That is not a number!");
        in.next();
      }
    }

    // now we are sure it's an int
    System.out.println("You entered " + number);
  }
}

 

Exception Variables

The exception variables (all named "e" above) have some functions you can call on them. Perhaps the most helpful is the "getMessage" function which returns any text that was passed to it when thrown


public class Messages {

    public static double slope(int x0, int y0, int x1, int y1) {
        // avoid dividing by zero
        if (x1 == x0) {
            throw new ArithmeticException("Line is vertical!");
        }

        return (double) (y1 - y0) / (double) (x1 - x0);
    }

    public static void main(String args[]) {
        // try to find slope 1
        try {
            double slope1 = slope(3, 4, 5, 6);
            System.out.println("Slope = " + slope1);
        } catch (ArithmeticException e) {
            System.out.println("The line 1 is vertical!");
        }

        // try to find slope 2
        try {

            double slope2 = slope(3, 4, 3, 6);
            System.out.println("Slope = " + slope2);
        } catch (ArithmeticException e) {
            System.out.println(e.getMessage());
        }
    }
}

The "printStackTrace" function prints where the program was when the exception occurred. These can be helpful when debugging!

Copyright © 2024 Ian Finlayson | Licensed under a Creative Commons BY-NC-SA 4.0 License.