Welcome to Lesson 16!


Learning Objectives

By the end of today's class, you should know...

  • How to describe the Java syntax for exception handling
  • How to catch and handle exceptions
  • How to generate code to handle user-input errors
  • Explain the difference between checked and unchecked exceptions


1. Exception Handling Basics

About Exceptions

  • A common way to develop a program is to first make the code work assuming that nothing unusual happens
  • After the usual case works, then you start to consider the exceptional cases like:
    • What if a users enter letters when you want numbers
    • What if a file your program needs was moved or deleted
  • Java helps you to write your code in just this way:
    1. You first write your code as if nothing unusual will happen
    2. When the usual case works correctly, you add code to handle exceptional cases
  • The mechanism to handle the unusual cases is known as exception handling
  • Exception handling is an important part of developing "solid" or "robust" programs
  • A solid or robust program means that you can run the program day after day
  • If crazy or unusual things happen, the program copes or at least recognizes that something is wrong
Cartoon: Every rule has an exception - that's the rule.

Terminology

  • Exception: an error condition that changes the normal flow of a program
  • Throw an exception: either the Java runtime or your code signals that something unusual happened
  • Catch an exception: take appropriate action to deal with an exception
  • Exception handler: the code that processes an exception

Example Program the Causes an Exception

1
2
3
4
5
6
public class ExceptionDemo {
    public static void main(String[] args) {
        String str = null;
        System.out.println(str.length());
    }
}

How Exceptions Work

  • An exception indicates an unusual or error condition has occurred
  • When an exception occurs, program control is transferred, or "thrown", to an area of code designated to handle the condition
  • Oftentimes the action is to display a stack trace like the following and exit the program:
    Exception in thread "main" java.lang.NullPointerException
            at ExceptionDemo.main(ExceptionDemo.java:4)
    
  • A stack trace like this is useful for debugging
  • However, you can, and often should, specify other actions


Exception Example

  • Many exceptions can occur because of user input errors
  • As an example, let us look at how you might handle user input errors
  • Following is a simple program showing the usual case

The Usual Case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Scanner;

public class TotalCalculator {
    public static void main(String[] args) {
        Scanner input  = new Scanner(System.in);
        double cost = 0, tax = 0;

        System.out.print("Enter cost: ");
        cost = input.nextDouble();
        System.out.print("Enter tax: ");
        tax = input.nextDouble();
        double total = cost + tax;
        System.out.println("Total: " + total);
        System.out.println("Thanks for paying!");
    }
    input.close();
}
  • The program works for the usual case but can fail on invalid data
  • What happens if the user enters a dollar sign before the numbers?
  • What happens if the user enters letters instead of numbers?
Enter cost: $100
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.nextDouble(Scanner.java:2387)
      at TotalCalculator1.main(TotalCalculator1.java:9)
  • The program prints the message shown above and then ends
  • This is not desirable behavior, so we need to catch and handle the exception
  • In Java, you catch and handle exceptions with a try-catch statement


Handling Exceptions

  • To catch and handle exceptions you use a try-catch statement
  • The code that can cause an exception goes inside a try block
  • Code to handle the exception goes inside a catch block
  • Syntax:
    try {
      // One or more statements that can throw an exception
    } catch(ExceptionType exceptionInstance) {
      // Exception handling code
      // Can have many catch blocks
    } finally { 
    // Executes regardless
}

Where:
  • ExceptionType: the type of exception
  • exceptionInstance: the name you make up for the exception object
  • Each try block is followed by one or more catch blocks
  • The finally block will always execute even if an exception is caught.
  • The following example shows how to handle exceptions with try and catch blocks

Example of Exception Handling

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

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

public class TotalCalculator {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
double cost = 0, tax = 0;

try {
System.out.print("Enter cost: ");
cost = input.nextDouble();
System.out.print("Enter tax: ");
tax = input.nextDouble();
double total = cost + tax;
System.out.println("Total: " + total);
} catch(InputMismatchException e) {
System.out.println(
"Error -- enter digits only!");
} catch(Exception e) {
System.out.println("\nSome other exception!");
} finally {
System.out.println("Goodbye!");
}
input.close();
}
}
  • The program works the same way in the normal case
  • When the uses enters bad data, our program can do something about it now:
    Enter cost: $10
    Error -- enter digits only!
    Goodbye!
  • Ideally, we want our program to:
    1. Describe the problem to the user
    2. Let the user correct the entry
  • However, our program does not allow the user to correct the problem yet
  • To allow the user to correct the problem, we must first understand an exception's flow of control
Exception flow chart

image source

Exception Flow of Control

  • When an exception occurs:
    1. The method stops processing at the point of failure
    2. The code creates an object with the error information
    3. The JVM tries to locate the exception handling code
    4. If the JVM finds the exception-handling code, the matching catch block is passed the exception object
    5. If the JVM does not find a matching catch block, it uses the general exception handler instead
  • General exception handler displays an error messages on the console like the following and then ends the program
Exception in thread "main" java.util.InputMismatchException
        at java.util.Scanner.throwFor(Scanner.java:819)
        at java.util.Scanner.next(Scanner.java:1431)
        at java.util.Scanner.nextDouble(Scanner.java:2335)
        at ReceiptCalculator.main(ReceiptCalculator.java:9)

 
  • Let us trace through the flow for our example code
    1. The user enters bad data and the main() method stops processing
      Enter cost: $100
      
    2. The scanner object code creates an exception object of type InputMismatchException
    3. the JVM passes the exception object to the catch statement:
      catch(InputMismatchException e)
    4. The exception handling code executes
    5. The finally block executes
    6. The program continues with the code after the list of try-catch-finally blocks
  • Note that the program does not execute the second input statement
  • As soon as the error occurs, the program jumps to the exception handler
  • Program execution continues after the exception handler code
excecution of try-catch-finally when there is an exception vs when there is not

Trying Again

  • To allow the user to try again when there is an error, we need to code a loop
  • One way is to use a looping statement like the following

Example of Verification Using a Loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.Scanner;
import java.util.InputMismatchException;
import java.util.NoSuchElementException;

public class TotalCalculator {
    public static void main(String[] args) {
Scanner input = new Scanner(System.in);
double cost = 0, tax = 0;

boolean more = true;
while (more) {
try {
System.out.print("Enter cost: ");
cost = input.nextDouble();
System.out.print("Enter tax: ");
tax = input.nextDouble();
double total = cost + tax;
System.out.println("Total: " + total);
more = false;
} catch(InputMismatchException e) {
System.out.println(
"Error -- enter digits only!");
input.nextLine(); // clear the buffer
} catch(Exception e) {
System.out.println("\nSome other exception!");
input.nextLine();
}
}
input.close();
System.out.println("Goodbye!"); //placed finally code outside loop
}
}
  • Running the program we see:
    Enter cost: 100
    Enter tax: $6
    Error -- enter digits only!
    Enter cost: 100
    Enter tax: 6
    Total: 106.0
    Goodbye!
    
  • The program now allows the user to correct their errors and continue
  • However, the code requires the user to reenter all the numbers
  • A better solution is to reenter only the number in error
  • To do this we need to loop on each statement that could cause an exception
  • This leads to duplicate code
  • To eliminate the duplicate code, we use a method to handle the entire data input process

Verifying in Methods

  • Using a method we can group our input and verification code and reduce duplication
  • Users can correct individual errors without having to redo all their entries
  • Also note how much simpler the code in main() appears

Example of Verification Using a Method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

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

public class TotalCalculator {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
double cost = 0, tax = 0;

cost = readDouble(input, "Enter cost: ");
tax = readDouble(input, "Enter tax: ");
input.close();

double total = cost + tax;
System.out.println("Total: " + total);

System.out.println("Thanks for shopping!");
}

/**
* Read a double from the console.
*
* @param input Scanner object to read from.
* @param prompt User prompt to show before input
* @return A double entered by the user.
*/
public static double readDouble(Scanner input,
String prompt) {
double number = 0;
boolean more = true;
while (more) {
try {
System.out.print(prompt);
number = input.nextDouble();
more = false;
} catch(InputMismatchException e) {
System.out.println(
"Error -- enter digits only!");
input.nextLine(); // clear the buffer
} catch(Exception e) {

                System.out.println("\nSome other exception!");
input.nextLine();

}
}
return number;
}
}
  • Note that there is still a loop in the readDouble() method -- can you see it?
  • Programmers commonly develop a library of static input methods like these

Improved output:

Enter cost: $10
Error -- enter digits only!
Enter cost: 10
Enter tax: 9%
Error -- enter digits only!
Enter tax: 9
Total: 19.0
Thanks for shopping!


Group Activity

  • By number, which lines are executed by the following if the user enters "$100" for the cost?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

import java.util.Scanner;
import java.util.InputMismatchException;
import java.util.NoSuchElementException; public class TotalCalculator2 { public static void main(String[] args) { Scanner input = new Scanner(System.in); double cost = 0, tax = 0; try { System.out.print("Enter cost: "); cost = input.nextDouble(); System.out.print("Enter tax: "); tax = input.nextDouble(); double total = cost + tax; System.out.println("Total: " + total); } catch(InputMismatchException e) { System.out.println( "Error -- enter digits only!"); } catch(NoSuchElementException e) { System.out.println("\nGoodbye!"); } finally {     System.out.println("Thanks for paying!");
        }
    }
}

Activity 16.1: Trying and Catching (10 pts)

  • The below program is adapted from a CIS 36A activity.
  • In the activity in 36A, we used hasNextDouble() to avoid a possible InputMismatchException.
  • In this activity, we will use try-catch instead.
  • Open a new file named Scores.java and copy and paste the below starter code into it.
/**
 * Scores.java
 * @author
 * CIS 36B, Activity 16.1
 */

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

public class Scores {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        ArrayList<Double> scores = new ArrayList<Double>();
        double score = 0.0;
        double sum = 0.0;
        int count = 0;
        System.out.println("Welcome! Enter a score or -1 to quit.\n");
        while(score >= 0.0) {
            count++;
            System.out.print("Enter score #" + count + ": ");
            score = input.nextDouble();
           
            if(score >= 0.0) {
                sum += score;
                scores.add(score);
            }
        }
        System.out.println("\nScores Summary:");
        System.out.println("\nSum of scores: " + sum);
        System.out.println("Number of scores: " + (count - 1));
        System.out.println("Average: " + (sum / (count - 1)));
        System.out.println("\nGoodbye");      
        input.close();
    }
}

  •   Now, run the code as shown in the sample output below, and verify that you get an InputMismatchException:
InputMismatchException thrown when entering &quot;ninety&quot; to the console
  • Let's write some code to handle this exception and provide the user another opportunity to correct the input error without needing to re-run the program.
  • Alter the code inside your while loop to contain a try-catch block. Note that finally is not required here.
  • Update your while loop to look like the following:
updated while loop with a try-catch
  • With this change, notice that there is an issue that occurs.
    • The counter variable gets off by one each time a user enters an incorrect input.
    • This error also causes an error in the average.
Welcome! Enter a score or -1 to quit.

Enter score #1: ninety
Error! Please enter numbers only not text.
Enter score #2: 90
Enter score #3: 80
Enter score #4: seventy
Error! Please enter numbers only not text.
Enter score #5: 70
Enter score #6: -1

Scores Summary:

Sum of scores: 240.0
Number of scores: 5
Average: 48.0

Goodbye!
  • Correct this error by altering the code above and inside of the loop (don't change the code below the loop).
  • Now, verify that you can get the below output when running your improved program:
Welcome! Enter a score or -1 to quit.

Enter score #1: ninety
Error! Please enter numbers only not text.
Enter score #1: eighty
Error! Please enter numbers only not text.
Enter score #1: 90
Enter score #2: 80
Enter score #3: seventy
Error! Please enter numbers only not text.
Enter score #3: 70
Enter score #4: -1

Scores Summary:

Sum of scores: 240.0
Number of scores: 3
Average: 80.0

Goodbye!
  • There are several exception types that exist in Java. So far, we have only worked examples of InputMismatchException.
  • Let's look at another common exception IndexOutOfBoundsException
  • Add this while loop below the code to display the average:
System.out.println("Average: " + (sum / (count - 1)));
       
    System.out.print("\nEnter the score # to view or 0 to quit: "); //using to 0 bc of array indices
    count = input.nextInt();
    while(count > 0) {
        System.out.println("Score " + (count) + ": " + scores.get(count - 1));
        System.out.print("Enter the next score#: ");
        count = input.nextInt();
    }
       
    input.close();

  • Note: Why am I using 0 as the sentinel value for the second loop?
  • Then, verify that you get the following exception when entering the inputs as shown:
IndexOutOfBounds exception

  • To anticipate this problem, we will need to place a try-catch inside the program.
  • Where would it go?
  • In this instance, we would need to place it around both statements that read in the user input (one above the loop and one inside the loop).
  • Instead, it would be preferable to write a method to handle this situation.
  • Add the following method to your program:
    public static int readScore(Scanner input, String prompt, ArrayList<Double> scores) {
        int number = 0;
        boolean more = true;
        while (more) {
            try {
                System.out.print(prompt);
                number = input.nextInt();
                if (number != 0) {
                    scores.get(number - 1);
                }
                more = false;
            } catch(IndexOutOfBoundsException e) {
                System.out.println("Please enter an index "
                        + "from 1 to " + scores.size() + "!");
               input.nextLine();
            }
        }
        return number;
    }
   
  • Now, call this method each time that a user input is required for the second loop.
  • Your while loop should now look as shown below:
Revised while loop that calls the method readScore
  • Run the program again using the below inputs to verify that your output is correct:
Welcome! Enter a score or -1 to quit.

Enter score #1: a hundred
Error! Please enter numbers only not text.
Enter score #1: 100
Enter score #2: ninety
Error! Please enter numbers only not text.
Enter score #2: 90
Enter score #3: 80
Enter score #4: 70
Enter score #5: 60
Enter score #6: -1

Scores Summary:

Sum of scores: 400.0
Number of scores: 5
Average: 80.0

Enter the score # to view or 0 to quit: 6
Please enter an index from 1 to 5!

Enter the score # to view or 0 to quit: 7
Please enter an index from 1 to 5!

Enter the score # to view or 0 to quit: -3
Please enter an index from 1 to 5!

Enter the score # to view or 0 to quit: 2
Score 2: 90.0

Enter the next score #: 6
Please enter an index from 1 to 5!

Enter the next score #: 3
Score 3: 80.0

Enter the next score #: 0
Goodbye!

When you are getting the correct output, upload Scores.java to Canvas.


Wrap up
  • Answer the Practice Exam Questions for this lesson on Canvas.


Upcoming Assignments:

  • Activity 16.1 due Thursday at 11:59pm
  • Lesson 16 Practice Exam Questions due Thursday at 11:59pm
  • Quiz 7 due Friday at 11:59pm
  • Peer Reviews of Lessons 15 and 16 Practice Exam Questions due Saturday at 11:59pm
  • Lab 8 due Monday at 11:59pm


~Have a Great Weekend!~