When Java programs encounter errors, they throw exceptions. You can see this if you try one of the following snippets of code:
Scanner in; String name = in.next();
int array [] = new int [5]; array[5] = 0;
int value = in.nextInt(); // when user enters a non-integer
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.
An exception is a programming construct specifically for handling errors.
There are three parts of an exception:
Try
Specifies a block of code in which an exception might occur. Code in the try block is executed until an exception happens.
Catch
Specifies a block of code to handle an exception in some way. Code in the catch block is only executed on an exception.
Throw
Creates a new exception, and transfers control to the nearest catch block that can be found. Exceptions can only be caught by a catch matching its type.
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.
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?
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.
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);
}
}
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.