Welcome to Lesson 16 - Part 2!


Learning Objectives

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

  • The difference among errors, checked exceptions and unchecked exceptions
  • Commonly used methods inherited from the Throwable class
  • How to use the throws clause when defining a method
  • How to throw an exception
  • How to use predefined Exception classes


More Exception Topics

Kinds of Exceptions

  • Java has three categories of exceptional conditions:
    1. Errors
    2. Checked exceptions
    3. Unchecked exceptions
  • These exceptions are organized in an inheritance hierarchy as shown below
exception inheritance hierarchy
        image source
  • Exceptions with RuntimeException as an ancestor are unchecked exceptions

Errors

  • Errors are exceptional conditions that are external to the application
  • Usually the application cannot recover from these types of errors
  • For example, suppose that while your program is reading from a file that the computer has a hardware failure or you write an application that causes the computer to run out of memory
  • You may want to alert the user, but there is little that the program can do to recover
Exception in thread "main" java.lang.StackOverflowError
    at java.lang.Integer.toString(Integer.java:402)
    at java.lang.Integer.toString(Integer.java:935)
    at java.lang.String.valueOf(String.java:2994)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
    at Graph.printPath(Graph.java:252)
    at Graph.printPath(Graph.java:252)
    at Graph.printPath(Graph.java:252)
    at Graph.printPath(Graph.java:252)

Checked Exceptions

  • A checked exception is an exceptional condition that a program should be able to recover from
  • The compiler checks that some mechanism is in place for receiving and processing the exception object
    • For this reason, it is known as a checked exception
    • For example, the below code will not compile:
          public static void main(String[] args) {
              File file = new File("abc");
              Scanner input = new Scanner(file);
          }
  • Java forces these types of problems to be provided for if they occur in one of two ways:
    • The programmer can update the method signature:
    public static void main(String[] args) throws IOException{
        File file = new File("abc");
        Scanner input = new Scanner(file);
    }
    • Alternately, the programmer can catch the exception:
    public static void main(String[] args) {
        try {
            File file = new File("abc");
            Scanner input = new Scanner(file);
        } catch(IOException e) {
            System.out.println(e); //invoking toString
        }
        
    }
    • Best practice: write code to allow the program to recover and continue if this type of exception occurs
  • An example of this type of exception is the FileNotFoundException
  • Normally, your program reads a file using a name supplied by the user
  • However, sometimes a user will supply a wrong name
  • In these cases your program should notify the user and ask for the correct name
    • In other words, good practice states to use a try-catch to recover from this error
    • Good practice dictates that all IOExceptions should be caught.
    public static void main(String[] args) {
        try {
            File file = new File("abc");
            Scanner input = new Scanner(file);
        } catch(FileNotFoundException e) {
            System.out.println(e); //invoking toString
        }  
    }

Unchecked (Runtime) Exceptions

  • An unchecked exception is usually the result of a programming error
  • For example, consider the example:
    String str = null;
    System.out.println(str.length());
    
  • This code will cause a NullPointerException
  • There is nothing that the program can do to recover from this error
    • The programmer needs to go back and fix the code
  • Thus, a NullPointerException was written as an unchecked exception
  • The compiler does not ensure there is a handler for unchecked exceptions built into the code
  • If an unchecked exception does not have an error handler, the general exception handler is used
  • The general exception handler produces a stack trace and then exits the program
  • Note in the below diagram that exceptions with RuntimeException as an ancestor are unchecked
    • Otherwise, they are checked exceptions

Hiearchy of checked and unchecked exception types
image source

Predefined Exception Classes

  • As we have seen, there are more exception classes than the single class Exception
  • Some of them are shown in the following tree
  • You can also write your own exceptions, which we will cover in the following section
  • However, you should use standard exceptions wherever possible
java.lang.Throwable
  |
  +--java.lang.Error (Unchecked)
  |
  +--java.lang.Exception (Checked)
       |
       +--java.lang.InterruptedException
       |
       +--java.io.IOException
       |
       +--java.lang.RuntimeException (Unchecked)
            |
            +--java.lang.ArithmeticException
            |
            +--java.lang.IllegalArgumentException
            |
            +--java.lang.IllegalStateException
            |
            +--java.lang.IndexOutOfBoundsException
            |
            +--java.lang.NullPointerException
            |
            +--java.util.NoSuchElementException
               |
               +--java.util.InputMismatchException
  • Throwable is the root class of all exceptions
    • Has the commonly used methods for all errors and exceptions
  • Error means that a serious problem has occurred
    • Applications cannot recover from these types of problems
  • Exception is the root class of exceptions that a reasonable program might catch
  • RuntimeException are errors caught by the JVM rather than the compiler
    • Usually due to program bugs
  • All exceptions other than Error and RuntimeException (and subclasses) are checked exceptions
    • The compiler forces you to deal with the exception
  • Predefined exceptions usually include a meaningful message that is retrieved with method getMessage():
    System.out.println(e.getMessage());

Commonly Used Methods of the Throwable Class

Method Description
getMessage() Returns the message associated with the exception, if available.
printStackTrace() Prints the stack trace to the standard error stream.
toString() Returns a string with the name of the exception class along with the message associated with the exception, if available.

What Exceptions Can Occur?

  • The are two usual ways to determine what exceptions can occur in a program
  • The first way is to try the code and see what happens
  • If we tried to code our TotalCalculator program without a try-catch:
    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!");
    
  • We found we could get a an exception like:
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)
  • This tells you that you need to code a try-catch for an InputMismatchException
  • Sometimes you might want to know what the possible exceptions are possible in your code
  • To find out, you can look up exceptions in the Java API
  • For example, you can look up the documentation for the Scanner method: nextDouble()
  • You then examine the list of possible exceptions and determine if and how they might occur in your program

More Information


Using the throws Clause

  • Anytime your program uses a method that can cause a checked exception or throws an unchecked exception, your program must do something with it.
  • You have two options:
    • Handle the exception using a try-catch
    • Throw the exception to another method or to the general exception handler
  • If you decide the method cannot handle the exception, you use a throws clause to send the exception to the caller
  • The general syntax for throwing an exception:
    [accessModifier] returnType methodName(parameterList)
        throws ExceptionType1, ExceptionType2,... {
        // statements
    }
    
  • Where:
    • accessModifier: determines which classes can access this method
    • returnType: the type of value returned by the method, or void for no return value
    • methodName: the name you make up for the method
    • parameterList: the types and names of the parameters
    • ExceptionTypeX: the type of exception a method throws
  • For example:

    public void printArrayFile(File f) throws IOException {
    PrintWriter pw = new PrintWriter(f)
    for (int i = 0; i < numElements; i++) {
    pw.println(array[i]);
    } pw.close();

    }
  • Throwing an exception is like passing the buck:
    • Requires the calling method to deal with the exception
  • The calling method can also throw the exception
  • Eventually some method should catch it or the general exception handler is called

How Exceptions are Propagated

  • When methods cannot handle an exception, that method should throw the exception
  • The user of the method can then decide how to handle the exception
  • Methods can call other methods that can throw an exception
  • This chain continues usually until one method catches the exception
  • If no method catches the exception, then the general exception handler will print an error message at runtime.
exception propogation

Example of Exception Propagation

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

public class ExceptionPropogation {

    public static void main(String[] args) {
        methodD();
    }

    public static void methodD() {
        try {
            methodC();
        } catch (Exception e) {
            System.out.println("Caught exception in D");
     System.out.println(e.getMessage());
 } } public static void methodC() throws Exception { methodB(); } public static void methodB() throws Exception { methodA(); } public static void methodA() throws Exception { throw new Exception("Throwing exception in A"); } }

Throwing Exceptions

  • You may be wondering how the code in Scanner and other classes generates an exception
  • The answer is the Java throw statement
  • Syntax:
    throw new ThrowableObject
  • Where:
    • ThrowableObject: the exception class to throw
  • For example:
    throw new Exception("Exception message");
  • Note that throw is a Java keyword, which should be followed by an Exception object.
    • In the above example, throw is followed by a call to the Exception constructor
  • Typically, you decide to throw an exception after checking for an error condition:
    if (cost < 0) {
        throw new Exception(
            "Cost cannot be less than 0");
    }
    
  • To demonstrate, let us throw an exception in an example program

Example Program Using throw

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

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();
if (cost < 0) {
throw new Exception(
"Cost cannot be less than 0");
}
System.out.print("Enter tax: ");
tax = input.nextDouble();
if (tax < 0) {
throw new Exception(
"Tax cannot be less than 0");
}
double total = cost + tax;
System.out.println("Total: " + total);
more = false;
} catch(Exception e) {
String message = e.getMessage();
System.out.println(message);
input.nextLine(); // clear the buffer
}
}
System.out.println("Thanks for paying!");
}
}
  • Note the use the getMessage() method to explain the error
  • There are a few other commonly used exception methods shown in the table below
  • Note that the line of code above to clear the buffer is not strictly necessary here as the tax value has already been read into the variable before throwing the exception.
  • However, in the prior example, it was required to clear the erroneous input from the buffer, which we were not able to read into the variable:
           try {
System.out.print("Enter cost: ");
cost = input.nextDouble(); //a String cannot be read into this variable
System.out.print("Enter tax: ");
tax = input.nextDouble(); //a String cannot be read into this variable
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 (required)
}

Writing a Method that Throws an Exception

  • When you write a method, you have the option to let this method throw an exception.
  • When writing a method that throws an exception, you should add a throws clause to the method signature
  • This alternation to the signature will cause an @throws tag to be added to the Javadoc comment
  • Then, as part of the method body, you should throw the stated exception:
    /**
     * Computes the sum of the values at two
     * indices of the ArrayList
     * @param scores the ArrayList of scores
     * @param index1 the first index
     * @param index2 the second index
     * @return the sum of the values at the two indices
     * @throws IndexOutOfBoundsException when
     * 0 > index1 || index1 >= size
     * 0 > index2 || index2 >= size
     */
    public static double getIndexSum
        (ArrayList<Double> scores, int index1, int index2) throws IndexOutOfBoundsException{
        if (index1 < 0 || index1 >= scores.size()) {
            throw new IndexOutOfBoundsException(index1
                    + " is outside the bounds of the ArrayList");

        } else if (index2 < 0 || index2 >= scores.size()) {
            throw new IndexOutOfBoundsException(index2
                    + " is outside the bounds of the ArrayList");

        } else {
            return scores.get(index1) + scores.get(index2);
        }
    }

When to Throw an Exception

  • Your program should only throw exceptions for exceptional conditions
  • This may happen when a method encounters a situation where it cannot complete its task
  • For example: the wrong file name was passed to the method opening a file
    • Throw an exception to the calling method, which can ask the user for the correct name


Group Activity:

  • Update the Below Method to Throw an IndexOutOfBoundsException rather than printing an error, in the case that the given index is outside the bounds of the array:

/**
     * Inserts a new element at the specified
     * index in the list array.
     * Resizes the list array if it is
     * at capacity before inserting
     * @param index the index at which to insert
     * @param str the element to insert
     */
    public void add(int index, E element)
    {
        if (index >= numElements) {
            System.out.println("Error: Cannot add element " + element
                    + " at index " + index + ".\nIndex is "
                    + "outside the bounds of the array list."
                    + "\nIndex: " + index + ", Size: " + numElements);
                return;
           
        } else if (atCapacity()) {
            reSize();
        }
        for (int i = numElements; i > index; i--) {
            array[i] = array[i-1];
        }
        array[index] = element;
        numElements++;
    }


Activity 16.2: More Scores (10 pts)

  • Open up your Scores.java from last activity and copy and paste the following code into it:
/**
 * Scores.java
 * @author
 * CIS 36B, Activity 16.2
 */

import java.util.Scanner;
import java.util.ArrayList;
import java.io.File;
import java.io.IOException;

public class Scores {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        ArrayList<Double> scores = new ArrayList<Double>();
        String fileName;
        boolean more = true;
        System.out.println("Welcome!\n");
       
        fileName = input.next();
        readScoresFromFile(scores, fileName);
               
        input.close();
        System.out.println("\nScores Summary:");
        System.out.println("\nSum of scores: " + computeSum(scores));
        System.out.println("Number of scores: " + scores.size());
        System.out.println("Average: " + (computeSum(scores) / scores.size()));
     
        input.close();
    }
   
    public static void readScoresFromFile
        (ArrayList<Double> scores, String fileName) {
        File file = new File(fileName);
        Scanner input = new Scanner(file);
   
        while(input.hasNextDouble()) {
                scores.add(input.nextDouble());
        }
       
        input.close();
       
    }
   
    public static double computeSum(ArrayList<Double> scores) {
        double sum = 0.0;
        for (int i = 0; i < scores.size(); i++) {
            sum += scores.get(i);
        }
        return sum;
    }
}

  • Next, create a file inside the same project folder in Eclipse.
  • The file should be named scores.txt
  • Copy and paste the below contents into scores.txt:
60
70
80
90
100

  • Notice that Scores.java will not compile.
  • You are required to throw an IOException because it is a checked exception
    • The compiler checks whether the appropriate method in your program throws the exception.
    • Note that unchecked exceptions are checked at runtime.
    • Thus, your code will compile if you do not throw the unchecked exception.
    • Instead, you will see the error when the code runs.
  • Fix the readScoresFromFile method so that it throws an IOException
  • Then, notice that a new error appears stating that the exception is not handled.
  • We will handle the exception in main where the name of the file is input.
  • Our goal is to allow the user the opportunity to try again when an incorrect file name is entered.
  • Thus, alter main to look like the following:
updated main method

  • Test out the updated main method by entering in the names of some files.
  • Verify that the program is working properly as shown below:

Welcome!

Enter the name of a file: scores
Invalid file name.
Enter the name of a file: scores.tx
Invalid file name.
Enter the name of a file: scores.txt

Scores Summary:

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

  • When your program is working as shown, submit Scores.java to Canvas.


Wrap up

  • Answer the Practice Exam Questions for this lesson on Canvas


Upcoming Assignments

  • Activity 16.2 due Tuesday at 11:59pm
  • Lesson 16, Part 2 Practice Exam Questions due Tuesday at 11:59pm
  • Lab 9 due next Monday at 11:59pm

~Have a Great Day!~