Exception Handling Intro

Things To Do First

Things To Do Later

Exception Handling

Exception is the object-oriented term for an error in a program. We call them "exceptions" because they are "exceptional circumstances" or "exceptional events" in a program. Exceptions are actually objects, and when an exception occurs while a program runs, we say that an exception is thrown. Exception handling refers to the code we write to make sure our program doesn't crash when an exception occurs. We often refer to this as catching an exception. We will only have a brief introduction to exceptions in this course, and will examine them in more detail next semester.

Important!
Exceptions fall into the category of Run-Time/Execution Errors.

There are many different kinds of exceptions, and each are modeled or represented by a particular class. There is a generic Exception class, but there are also more specific exception classes. For example, IOException handles general exceptions for Input/Output streams, and EOFException is a more specific form of the IOException that is thrown when you reach the end of a data file. NumberFormatException is a special kind of exception that is thrown when you try to convert one type of data to another, but an invalid value is found. There are hundreds of other kinds of exceptions, and you'll encounter many as you learn Java.

One problem we've noticed a lot when working with user inputs is that if the user types in an invalid value, the program crashes. For example, try the following program:

import java.util.*;

public class TestExceptions {

    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);

        System.out.print("Enter a number: ");
        int number = in.nextInt();

        System.out.println("Root: " + Math.sqrt(number));
    }
}

As long as the user types any number, the program works fine. But what if they type a string value? Try your program and enter the value "two" at the prompt. You should see the following errors:

Enter a number: two
Exception in thread "main" java.util.InputMismatchException
        at java.util.Scanner.throwFor(Scanner.java:840)
        at java.util.Scanner.next(Scanner.java:1461)
        at java.util.Scanner.nextInt(Scanner.java:2091)
        at java.util.Scanner.nextInt(Scanner.java:2050)
        at TestExceptions.main(TestExceptions.java:10)
                

This is a stack trace and it's telling you what exception occurred and where. In this example, the first line is saying that an exception occured in the main() method, and that this exception was an "InputMismatchException". At the bottom of the stack trace, we can see that the problem started on line 10 of the main() method. In my code, that's the line

int number = in.nextInt();

On the next line up in the stack trace, it indicates that the Scanner.nextInt() method generated the error. All of the nextX() methods (e.g. nextInt(), nextDouble(), etc.) will throw this exception if they receive a value that a valid numeric. For example, only digits and the decimal point are acceptable for doubles and only digits are acceptable for ints.

This means that if the user types letters or symbols in our program, an InputMismatchException is thrown.

We can't assume that are users will always type valid input all of the time. Sometimes users forget to read instructions, sometimes they forget what the instructions are, and sometimes they're just daft! We must never assume that user input will always be correct, so a good program always uses various techniques to ensure that invalid inputs don't crash a program. One of these techniques is exception handling.

One of the main components of exception handling is the try-catch block. This is a special code structure that works in the following way:

try {
	// code goes here that might
	// throw an exception
	// i.e.  "try this code"
} catch (Exception ex) {
	// code goes here that handles
	// the exception
	// you could display a message, 
	// exit the program, etc.
	// i.e. "if the code I tried throws 
	// an exception, catch it here and
	// do whatever"
}
Important!
A try-block must be accompanied by a catch-block or a finally-block. You'll learn more about finally-blocks in term 2. A try-block can have multiple catch-blocks, which you'll also learn about in term 2.

You'll notice a parameter variable in the header of the catch-block. This variable "ex" contains the Exception object that was thrown. There are a few bits of information in this object that you might want to look at. For example, some Exceptions come with an error message (although it's not always helpful!) which you can view using the ex.getMessage() method. You can also print the entire exception object's string value by invoking the ex.toString() method (which is also implicitly called when you type something like System.out.println(ex);)

Modify your example so it uses a try-catch block:

import java.util.*;

public class InputErrors {

    public static void main(String[] args) {

        try {
            Scanner in = new Scanner(System.in);

            System.out.print("Enter a number: ");
            int number = in.nextInt();

            System.out.println("Root: " + Math.sqrt(number));

        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
}

When you re-compile and run you're program, you'll notice that you don't get the error message on the console. Instead, you simply see:

null

In this case, the error message inside this Exception object is not very helpful. The value "null" is used when an object contains nothing at all, so seeing "null" here means that the Exception object's message (String object) is empty. Change the println() in the catch-block to print the entire exception object, instead:

System.out.println(ex);

This time when you run the program you see:

java.util.InputMismatchException

This is the fully qualified name of the Exception object (that means that you see the name of the class, preceeded by its package name). Again, this is not too helpful, especially to a non-technical person using your program!

Now edit the same line, but this time print a nice, friendly error message that would make more sense to the user:

System.out.println("Error: Not a whole number. Exiting..");

When you re-compile and run your program, you'll notice that you don't get the error messages on the console. Instead, you get a message with Error: Not a whole number. Exiting...

The user still gets an error message, but at least it's a more friendly error message than seeing the other stuff!

We'll use more exception handing in different ways throughout the course, but now you at least have an introduction to it.

Important!
Never leave the catch-block empty!! If you do this, and there are other exceptions happening in your code that you didn't think about, you'll never find out about them because they'll be caught by your catch-block: since it's empty, nothing will show on the screen; you'll receive no indication at all that an exception has occurred! Always make sure you have something in your catch block, even if it's just ex.printStackTrace(); until you get around to creating some nicer error messages.

Exercise

[solutions]

Revisit your program that calculates the difference between 2 amounts of time.

Exceptions With Wrapper Classes

Every primitive type in Java has a wrapper class associated with it. This class is used to "wrap" or "box" primitive values into objects (we will do this in the second course in programs that require values as objects instead of primitives).

In most cases wrapper classes are the same name as the primitive name except they start with an upper-case letter (which should be obvious, since they're classes!). The two exceptions are the Integer class (the wrapper for int primitive types) and the Character class (the wrapper for char primitive types).

In addition to being able to wrap or box primitives into objects, wrapper classes also contain a variety of useful methods. One example is the "parse" methods. Parse methods accept a String argument and return that string as a numeric value. For example, the Double wrapper class contains a parseDouble() method that accepts a String argument and returns that String as a primitive double value. The Integer wrapper class contains a method called parseInt() that accepts a String argument and returns that String as an int primitive.

When using the parse methods from the wrapper classes, we notice that exceptions are thrown when we try to give them a value that can't be converted into the appropriate data type. For example, try the following code in a new program:

String strValue1 = "2.5";
String strValue2 = "two";
int intValue = Integer.parseInt(strValue2);

Answer the following questions:

[solutions]

a) What is the name of the exception that is thrown when this code runs, and what line causes the exception?

b) What happens if you use strValue1 as the parseInt() method's argument instead of strValue2? Why does this happen? For example:

int intValue = Integer.parseInt(strValue1);

c) What exception is caused if you change the last line to:

double dblValue = Double.parseDouble(strValue2);

d) What is the output of the following code?

String strValue1 = "2.5";
double dblValue = Double.parseDouble(strValue1);
System.out.println(dblValue);

Exercises

[solutions]

1. Write a program that calculates and displays a sales person's sales commissions for the day. A user will need to enter the amount of the sales made and the commission rate that the sales person gets. Then the program should display the amount of the sales commissions to pay the sales person.

a) Write the code using the Scanner class. Use exception handling to show a friendly error message to the user before exiting the program, in case the user enters invalid data, such as a String or other non-numeric value.

b) Modify the program in 1a: add a single line of code AFTER the catch-block closing brace, before the end of the main method:

System.out.println("Thank you!");

Run the program. Do you see the message when no errors occur? Do you see the message when the user types invalid data?

More Data Validation

How can we use exception handling to validate data entered by the user? There are a few options. For one, you can use the try-catch block to catch invalid data entry and put it inside a loop:

double data = -1;
while (data < 0) {
   try {
      System.out.print("Enter a positive number: ");
      data = in.nextDouble();
      // if we get to this point, then everything's fine!
      if (data < 0)
          System.out.println("Error! You must enter a positive number.");
      else
          // do whatever you want with your valid data
   } catch (Exception ex) {
      System.out.println("Error! Invalid value entered.\nPlease try again.");
      // purge input buffer here
      in.nextLine();
   }
}

In the example above, we initialize data with -1. Since we're looking for a positive value, this is invalid right away. This allows us into our loop, which executes as long as data is negative. Once inside the loop, we ask the user for a value, and they enter it.

Two things can happen here: 1. the data is a valid numeric (may or may not be positive, though; we deal with that later) or 2. the data is not a valid numeric. In the second case, the program stops here and jumps to the catch-block, where it prints an error message. The value of data hasn't changed, it's still -1, so when we iterate back up to the while condition, it's still true. This allows us back into the loop where we can prompt the user again.

If the data entered is a valid numeric, no exception is thrown, however we still wish to make sure that the data meets the required criteria: It must be a positive number. We use an if-statement here to check and, if the data is not positive, an error message is displayed. After that, the program skips the else and the catch block, then iterates back up to the top of the loop. The value of data is still negative, so the loop continues again.

If the data is valid and positive, then the else block will be reached. Here is where you can do whatever it is you want with your valid data. The else block is optional: maybe if the data is valid, you would like to just move on with the program and ask for the next value. In that case, the else block is not needed; the loop condition will no longer be true and the program will just accept the valid data and move on to the next piece of code.

When you learn to write your own methods, you can modularize this piece of code and make it re-usable. This will be one of your tasks in that future lesson :)

Exercise

[solutions]

Write a program that asks the user to enter a department number. The department number must be between 1 and 100, inclusive. If the department number is invalid, display the error message "Error: Department# must be 1 to 100, inclusive." If the user enters a value that is not a valid int, display the error message "Error: Department# must be only digits." Prompt the user repeatedly until the user gets the department number correct. After a valid department number is entered, display the department name: any department number less than 20 is Sales; 20 up to but not including 25 is Manufacturing; 25 up to 32 is Inventory; 33 up to 40 is Administration; 41, 42, and 43 are Maintenance; 44 to 47 is Human Resources, and anything that's 48 and higher is Management. The department name should ONLY be displayed if there are no errors.