Download Chapter 15

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Chapter 15 - Exception Handling











Using try and catch Blocks to Handle "Dangerous" Method Calls
NumberFormatException
Line Plot Example
try and catch Blocks - More Details
Two Types of Exceptions - Checked and Unchecked
Unchecked Exceptions
Checked Exceptions
Using API Documentation when Writing Exception-Handling Code
When a try Block Throws Different Types of Exceptions
Generic catch Block With Exception Class
Multiple catch Blocks and Multiple Exceptions Per Block

Understanding Exception Messages
Using throws to Postpone the catch

Automatic Cleanup with Try-With-Resources

1
Using try and catch Blocks to Handle
"Dangerous" Method Calls


Some API method calls are "dangerous" in that they
might possibly lead to a runtime error.
Example of a "safe" API method call (no runtime error
possible):
System.out.println(<expression>)

Example of an API method call that might lead to a
runtime error:
Integer.parseInt(<string>)

Technique for handling such runtime errors:

Use exception handling. More specifically, surround the
method call with a try block and insert a catch block
immediately after the try block.
2
Using try and catch Blocks to Handle
"Dangerous" Method Calls

3
Syntax for try and catch blocks:
try
{
Normally, one or more of these statements will be a
<statement(s)>
"dangerous" API method call or constructor call.
}
catch (<exception-class> <parameter>)
{
The exception class should match the type of
<error-handling-code>
exception that the try block might throw.
}

Example try and catch code fragment:
try
{
quantity = Integer.parseInt(userEntry);
}
catch (NumberFormatException nfe)
{
System.out.println("Invalid quantity entered." +
" Must be a whole number.");
}
Using try and catch Blocks to Handle
"Dangerous" Method Calls

Semantics for previous slide's try and catch code
fragment:

If the userEntry string contains all digits, then:



The int version of userEntry is assigned into quantity.
The JVM skips the catch block and continues below it.
If the userEntry string does not contain all digits, then:


The parseInt method throws a NumberFormatException
object.
The JVM looks for a catch block that will catch the thrown
exception object; that is, it looks for a matching catch block.
If it finds one, it executes it and continues below the catch
block. If there's no matching catch block, the program
crashes.
4
NumberFormatException



The NumberFormatException is well named
because it's thrown when a number's format is
inappropriate.
More specifically, it's thrown by one of the parse
methods (Integer.parseInt, Long.parseLong,
Double.parseDouble, etc.) when there's an
attempt to convert a string to a number and the
string's characters don't form a valid number.
These code fragments throw
NumberFormatExceptions:
int numOfPages = Integer.parseInt("962.0");
double height = Double.parseDouble("1.76m");
5
Line Plot Example

6
This program plots a line by reading in a series of point coordinate positions. It works
fine as long as the user enters valid input. But with invalid input, the program crashes.
Add code so as to avoid those crashes.
import java.util.Scanner;
public class LinePlot
{
private int oldX = 0; // oldX, oldY save the previous point
private int oldY = 0; // The starting point is the origin (0,0)
//*************************************************************
// This method prints a line segment from the previous point
// to the current point.
public void plotSegment(int x, int y)
{
System.out.println("New segment = (" + oldX + "," + oldY +
")-(" + x + "," + y + ")");
oldX = x;
oldY = y;
} // end plotSegment
Line Plot Example
//*************************************************************
public static void main(String[] args)
{
Scanner stdIn = new Scanner(System.in);
LinePlot line = new LinePlot();
String xStr, yStr;
// coordinates for a point (String form)
int x, y;
// coordinates for a point
System.out.print("Enter x & y coordinates (q to quit): ");
xStr = stdIn.next();
reads a group of characters
while (!xStr.equalsIgnoreCase("q"))
and stops at whitespace
{
yStr = stdIn.next();
x = Integer.parseInt(xStr);
y = Integer.parseInt(yStr);
line.plotSegment(x, y);
System.out.print("Enter x & y coordinates (q to quit): ");
xStr = stdIn.next();
} // end while
} // end main
} // end class LinePlot
7
try and catch Blocks - More Details




Deciding on the size of your try blocks is a bit of an art.
Sometimes it's better to use small try blocks and sometimes it's
better to use larger try blocks.
Note that it's legal to surround an entire method body with a
try block, but that's usually counterproductive because it makes
it harder to identify the "dangerous" code.
In general, you should make your try blocks small so that your
"dangerous" code is more obvious.
However, if a chunk of code has several "dangerous"
method/constructor calls:

Adding a separate try-catch structure for each such call might

result in cluttered code.
To improve program readability, consider using a single try block
that surrounds the calls.
9
try and catch Blocks - More Details



In our LinePlot program solution, we surrounded the
two parseInt statements with a single try block
because they were conceptually related and physically
close together. We also included the
line.plotSegment() call within that same try
block. Why?
Our single try block solution is perfectly acceptable,
but wouldn't it be nice to have a more specific
message that identified which entry was invalid (x, y,
or both)?.
To have that sort of message, you'd have to have a
separate try-catch structure for each parseInt
statement.
10
try and catch Blocks - More Details



If an exception is thrown, the JVM immediately jumps out of the
current try block and looks for a matching catch block. The
immediacy of the jump means that if there are statements in the
try block after the exception-throwing statement, those
statements are skipped.
The compiler is a pessimist. It knows that statements inside a
try block might possibly be skipped, and it assumes the worst.
It assumes that all statements inside a try block get skipped.
Consequently, if there's a try block that contains an assignment
for x, the compiler assumes that the assignment is skipped. If
there's no assignment for x outside of the try block and x's
value is needed outside of the try block, you'd get this
compilation error:
variable x might not have been initialized

If you get that error, you can usually fix it by initializing the
variable prior to the try block.
11
try and catch Blocks - More Details

12
This method reads a value from a user, makes sure it's an integer, and returns it.
Note the compilation errors. What are the fixes?
public static int getIntFromUser()
{
Scanner stdIn = new Scanner(System.in);
String xStr;
// user entry
boolean valid; // is user entry a valid integer?
int x;
// integer form of user entry
System.out.print("Enter an integer: ");
xStr = stdIn.next();
do
{
try
{
valid = false;
x = Integer.parseInt(xStr);
valid = true;
}
catch (NumberFormatException nfe)
{
System.out.print("Invalid entry. Enter an integer: ");
xStr = stdIn.next();
}
compilation error: variable valid might not have been initialized
} while (!valid);
return x;
} // end getIntFromUser
compilation error: variable x might not have been initialized
13
Two Types of Exceptions - Checked and Unchecked

There are two types of exceptions – checked and
unchecked.



Checked exceptions are required to be checked with a trycatch mechanism.
Unchecked exceptions are not required to be checked with a
try-catch mechanism (but, as an option, unchecked
exceptions may be checked with a try-catch mechanism).
How can you tell whether a particular exception is
classified as checked or unchecked?


To find out if a particular exception is checked or unchecked,
look up its associated class in the API documentation.
On the class's API page, look at its class hierarchy tree. If
you find that the class is derived from the
RuntimeExeption class or from the Error exception
class, then it's an unchecked exception. Otherwise, it's a
checked exception.
14
Two Types of Exceptions - Checked and Unchecked
Class Hierarchy For Exception Classes
Throwable
Error
Exception
unchecked exceptions for
system errors (e.g.,
VirtualMachineError)
RuntimeException
checked exceptions
(e.g., IOException)
unchecked exceptions
(e.g., NumberFormatException,
ArithmeticException)
The parseInt, parseLong, parseDouble, etc. methods
all throw a NumberFormatException object.
Unchecked Exceptions


As you know, unchecked exceptions are not required
to be checked with a try-catch mechanism.
However, at runtime, if an unchecked exception is
thrown and not caught, then the program will crash
(terminate ungracefully).
How to handle code that might throw an unchecked
exception:

Use a try-catch mechanism (see prior GetIntFromUser
example).
or

Don't attempt to catch the exception, but write the code
carefully so as to avoid the possibility of the exception being
thrown (see upcoming example).
15
Unchecked Exceptions

The following method attempts to remove a specified student
from a list of student names. The list of student names is stored
in an ArrayList instance variable named students.
public void removeStudent(int index)
{
students.remove(index);
} // end removeStudent


The students.remove method call is dangerous because it
throws an unchecked exception,
IndexOutOfBoundsException, if its argument holds an invalid
index.
On the upcoming slides, we address that problem by providing
improved versions of the removeStudent method.
16
Unchecked Exceptions

Improved removeStudent method using a trycatch mechanism:
public void removeStudent(int index)
{
try
{
students.remove(index);
}
catch (IndexOutOfBoundsException e)
{
System.out.println(
"Can't remove student because " + index +
" is an invalid index position.");
}
} // end removeStudent
17
Unchecked Exceptions

Improved removeStudent method, using careful
code:
public void removeStudent(int index)
{
if (index >= 0 && index < students.size())
{
students.remove(index);
}
else
{
System.out.println(
"Can't remove student because " + index +
" is an invalid index position.");
}
} // end removeStudent
18
Checked Exceptions


If a code fragment has the potential of throwing a
checked exception, then the compiler requires that
the code fragment has an associated try-catch
mechanism. If there is no associated try-catch
mechanism, then the compiler generates an error.
The program on the next slide contains code that
might possibly throw a checked exception and there's
no try-catch mechanism. Thus, it generates a
compilation error. What code should be added to fix
the program?
19
Checked Exceptions
/*************************************************************
* CreateTimestampFile.java
* Dean & Dean
*
* This program creates a file, using a timestamp for its name.
*************************************************************/
import java.util.Date;
import java.nio.file.*; // for Path, Paths, Files
public class CreateTimestampFile
{
public static void main(String[] args)
{
String timestamp; // current date and time
String filename; // use timestamp for this filename
Path path;
// file location
unknown API constructor call
timestamp = new Date().toString();
filename =
timestamp.replaceAll(" ", "_").replaceAll(":", "_") + ".txt";
path = Paths.get(filename);
unknown API method calls
Files.createFile(path);
System.out.println(filename + " created.");
} // end main
} // end CreateTimestampFile class
20
Using API Documentation when Writing ExceptionHandling Code


Whenever you want to use a method or constructor from one of
the API classes and you're not sure about it, you should look it
up in the API documentation so you know whether to add
exception-handling code.
More specifically, use the API documentation to figure out these
things:

Can the method/constructor call possibly throw an exception?


On the API documentation page for the method/constructor, look for a
throws section. If there's a throws section, that means the
method/constructor can possibly throw an exception.
If the method/constructor call throws an exception, is it checked or
unchecked?



On the API documentation page for the method/constructor, drill down
on the exception class's name.
On the API documentation page for the exception class, look at the
exception class's class hierarchy.
If you find RuntimeException is an ancestor of the exception, then
the exception is an unchecked exception. Otherwise, it's a checked
exception.
21
Using API Documentation when Writing ExceptionHandling Code


If the method/constructor call can possibly throw a
checked exception, you must add a try-catch
mechanism to handle it.
If the method/constructor call can possibly throw an
unchecked exception, you should read the API
documentation to figure out the nature of the
exception. And then, depending on the situation, 1)
use a try-catch mechanism or 2) use careful code
so that the exception won't be thrown.
22
Using API Documentation when Writing ExceptionHandling Code

Handling the three "unknown" API method calls in the
CreateTimestampFile Program:


new Date()

Instantiates a Date object that holds the current time.

No exceptions are thrown.
Paths.get(filename)



Returns a Java Path object, which defines a file and its directory path.
Throws an unchecked InvalidPathException if its filename
argument contains characters that are illegal in a filename and path.
Files.createFile(path)




Creates a file stored in auxiliary memory.
Throws a checked IOException if there's an I/O problem, like a corrupt
hard disk.
Throws a checked FileAlreadyExistsException if there's an
attempt to create a file that already exists.
Throws 2 unchecked exceptions - UnsupportedOperationException
and SecurityException, which can be ignored for this program.
23
Improved CreateTimestampFile Program
import java.util.Date;
import java.io.IOException;
import java.nio.file.*; // for Path, Paths, Files
public class CreateTimestampFile
{
public static void main(String[] args)
{
String timestamp; // current date and time
String filename; // use timestamp for this filename
Path path;
// file location
timestamp = new Date().toString();
filename = timestamp.replaceAll(" ", "_").replaceAll(":", "_") + ".txt";
path = Paths.get(filename);
We need a try block for the 2 checked exceptions FileAlreadyExistsException.
try
IOException and
{
Files.createFile(path);
System.out.println(filename + " created.");
}
catch (IOException e)
{
System.out.println("File I/O error");
}
} // end main
} // end CreateTimestampFile class
Since IOException is a superclass
of
FileAlreadyExistsException,
we can use the single
IOException parameter to catch
both types of thrown exceptions.
24
25
When a try Block Throws Different Types of Exceptions

If several statements within a try block can possibly
throw an exception and the exceptions are of
different types, you should:

Provide a generic catch block that handles every type of
exception that might be thrown.
or

Provide a sequence of specific catch blocks, with each
catch block handling a different exception type or group of
exception types.
Generic catch Block with Exception Class

How to provide a generic catch block:


Define a catch block with an Exception parameter, e.
Optionally, inside the catch block, use the e exception object
to call:



getClass().getName(), which returns the class name of the
thrown exception object.
getMessage(), which returns a description of the thrown
exception.
For example:
catch (Exception e)
{
System.out.println(e.getClass().getName());
System.out.println(e.getMessage());
}
26
Generic catch Block with Exception Class





In a catch block heading, if you use an Exception parameter,
then all thrown exceptions will match up with the Exception
parameter. Why?
A thrown exception will be caught by a catch block if the thrown
exception's class equals the catch heading's parameter's class or
the thrown exception's class is a subclass of the catch heading's
parameter's class.
Since every thrown exception's class is a subclass of the
Exception class, all thrown exceptions will match up with a
generic Exception parameter.
The ReadFromFile program (on the next slide) illustrates how to
use a generic catch block to handle multiple types of thrown
exceptions.
The program opens a user-specified file and prints all the lines in
that file.
27
Generic catch Block with Exception Class
/***********************************************************
* ReadFromFile.java
* Dean & Dean
*
* This opens an existing text file and prints its lines.
***********************************************************/
import java.util.Scanner;
import java.nio.file.Paths;
public class ReadFromFile
{
public static void main(String[] args)
{
Scanner stdIn = new Scanner(System.in);
Scanner fileIn;
// file handler
String filename;
// user-specified file name
String line;
// line of text
28
Generic catch Block with Exception Class
System.out.print("Enter a filename: ");
try
This
{
Scanner
filename = stdIn.nextLine();
constructor
fileIn = new Scanner(Paths.get(filename));
throws a
while (fileIn.hasNext())
checked
{
exception.
line = fileIn.nextLine();
System.out.println(line);
}
} // end try
catch (Exception e)
{
System.out.println(
"Thrown exception: " + e.getClass().getName());
System.out.println(
"Exception message: " + e.getMessage());
}
} // end main
} // end ReadFromFile class
29
Generic catch Block with Exception Class

Sample session #1:
Enter a filename: Einstein.*
Thrown exception: java.nio.file.InvalidPathException
Exception message: Illegal char <*> at index 9: Einstein.*

Sample session #2:
Enter a filename: Einstein
Thrown exception: java.nio.file.NoSuchFileException
Exception message: Einstein

Sample session #3:
Enter a filename: Einstein.txt
A scientific theory should be as simple as possible,
but not simpler.
30
Generic catch Block with Exception Class

The ReadFromFile program contains quite a few
operations that can potentially throw an exception:





Paths.get throws an InvalidPathException (an unchecked
exception).
The Scanner constructor throws an IOException (a checked exception).
hasNext throws IllegalStateException (an unchecked exception).
nextLine throws IllegalStateException and
NoSuchElementException (both unchecked exceptions).
The program handles all of the exceptions above
(checked and unchecked) with a generic catch block
and its getClass().getName() and
getMessage() method calls.
31
32
Multiple catch Blocks and Multiple Exceptions Per Block


When a try block might throw more than one type of exception,
instead of providing one generic catch block, you can provide a
sequence of catch blocks, with each catch block handling a
different exception type or group of exception types.
For example:
catch (InvalidPathException | NoSuchFileException e)
{
System.out.println("Filename invalid or not found.");
} // end catching exceptions user can handle
catch (Exception e)
{
System.out.println(e.getClass() + ":");
System.out.println(e.getMessage());
}

What is a benefit of using multiple catch blocks rather than a single
generic catch block?
33
Multiple catch Blocks and Multiple Exceptions Per Block
/*************************************************************
* ReadFromFile2.java
* Dean & Dean
*
* This opens an existing text file and prints its lines.
*************************************************************/
import java.util.Scanner;
import java.nio.file.*;
// Paths, specific exceptions
public class ReadFromFile2
{
public static void main(String[] args)
{
Scanner stdIn = new Scanner(System.in);
Scanner fileIn;
// file handler
boolean validUserEntry = false;
do
{
System.out.print("Enter a filename: ");
34
Multiple catch Blocks and Multiple Exceptions Per Block
try
{
fileIn = new Scanner(Paths.get(stdIn.nextLine()));
// Above statement succeeds only when valid user input
validUserEntry = true;
while (fileIn.hasNext())
{
System.out.println(fileIn.nextLine());
}
} // end try
catch (InvalidPathException | NoSuchFileException e)
{
System.out.println("Filename invalid or not found.");
} // end catching exceptions user can handle
catch (Exception e)
Rules for multiple exceptions
{
System.out.println(e.getClass() + ":"); in a catch block heading:
System.out.println(e.getMessage());
1. Use a single vertical bar to
}
separate the parameters.
} while (!validUserEntry);
2. The exception parameters
} // end main
must not be in the same
} // end ReadFromFile2 class
class hierarchy tree.
Multiple catch Block Execution Sequence



If multiple catch blocks are used, the first catch block that
matches the type of the exception thrown is the one that is
executed; the other catch blocks are then skipped.
Whenever you use more than one catch block after a given
try block, and one catch block's exception class is derived
from another catch block's exception class, to avoid a
compilation error, you must arrange the catch blocks with the
more general exception classes at the bottom (the superclasses
go at the bottom).
For example, in the prior ReadFromFile2 program, you must put
the Exception catch block at the bottom because the
Exception class is a superclass of the
InvalidPathException and NoSuchFileException
classes.
35
Multiple catch Block Development



The original ReadFromFile program shows how you
can use the compiler’s knowledge and experiment
with a simple generic catch block to discover the
types of exceptions that might be thrown by various
forms of bad input.
Once you have discovered all the types of exceptions
that bad user entries might generate, you can put
these particular exceptions into additional catch
block(s) inserted before the generic catch block.
Then, modify the code so that if a bad entry occurs,
the program prints a short error message, repeats the
original prompt, and prompts for new input.
36
Understanding Exception Messages



As you know, if your code involves a checked
exception being thrown, you must include a trycatch mechanism for that code. Without the trycatch mechanism, your program won't compile
successfully.
On the other hand, if your code involves an
unchecked exception being thrown, it's optional
whether you include a try-catch mechanism for
that code. Without it, your program will compile
successfully, but if an exception is thrown, your
program will crash.
If such a crash occurs, the JVM prints a runtime error
message that describes the thrown exception.
37
Understanding Exception Messages
import java.util.Scanner;
public class NumberList
{
private int[] numList = new int[100]; // array of numbers
private int size = 0;
// number of numbers
//***************************************
public void readNumbers()
{
Scanner stdIn = new Scanner(System.in);
String xStr;
// user-entered number (String form)
int x;
// user-entered number
System.out.print("Enter a whole number (q to quit): ");
xStr = stdIn.next();
while (!xStr.equalsIgnoreCase("q"))
{
x = Integer.parseInt(xStr);
numList[size] = x;
size++;
System.out.print("Enter a whole number (q to quit): ");
38
Understanding Exception Messages
xStr = stdIn.next();
} // end while
} // end readNumbers
//***************************************
public double getMean()
{
int sum = 0;
for (int i=0; i<size; i++)
{
sum += numList[i];
}
return sum / size;
} // end getMean
//***************************************
public static void main(String[] args)
{
NumberList list = new NumberList();
list.readNumbers();
System.out.println("Mean = " + list.getMean());
} // end main
} // end class NumberList
39
Understanding Exception Messages

40
The NumberList program compiles and runs, but it's
not very robust. See below:
thrown exception
If this happens:
Approximate error message:
User enters a
non-integer
(e.g., hi).
Exception in thread "main" java.lang.NumberFormatException:
For input string: "hi"
at java.lang.Integer.parseInt(Integer.java:468)
at NumberList.readNumbers(NumberList.java:28)
at NumberList.main(NumberList.java:73)
User
immediately
enters q to quit.
Exception in thread "main" java.lang.ArithmeticException:
/ by zero
at NumberList.getMean(NumberList.java:49)
at NumberList.main(NumberList.java:58)
User enters
more than 100
numbers.
Exception in thread "main"
java.lang.ArrayIndexOutOfBoundsException: 100
at NumberList.readNumbers(NumberList.java:29)
at NumberList.main(NumberList.java:73)
callstack
trace
Understanding Exception Messages



41
As part of a runtime error, the JVM prints the exception
that was thrown and then prints the call-stack trace.
The call-stack trace shows the methods that were
called prior to the crash.
If you perform integer division with a denominator of
zero, the JVM throws an ArithmeticException
object.
If you access an array element with an array index
that's < 0 or >= the array's size, the JVM throws an
ArrayIndexOutOfBoundsException object.
Using throws to Postpone the catch




Suppose you have a utility method that might throw an exception,
but the original causes of that exception are in the calling
methods.
Or suppose you have a non-void return method that sometimes
throws an exception and that method’s exception handler cannot
know what value to have the method return.
Then it’s appropriate to move the try and catch blocks from the
called method back to the calling method(s). To do this, append
throws <exception-type> to the called method’s header.
For example, suppose you want a variation of the previous
removeStudent method to return the removed student’s name:
public String removeStudent(int index)
throws IndexOutOfBoundsException
{
return students.remove(index);
} // end removeStudent
42
Using throws to Postpone the catch
43
Scanner stdIn = new Scanner(System.in);
String[] names = {"Caleb", "Izumi", "Mary", "Usha"};
StudentList2 studentList = new StudentList2(names);
int index;
boolean reenter;
do
{
System.out.print("Enter index of student to remove: ");
index = stdIn.nextInt();
try
{
System.out.println(
"removed " + studentList.removeStudent(index));
reenter = false;
}
catch (IndexOutOfBoundsException e)
{
System.out.print("Invalid entry. ");
reenter = true;
}
} while (reenter);
Cleanup With finally Block



Sometimes you need to provide cleanup code that executes regardless of
whether an exception is thrown. For example, after writing to a file, you
must close the file to complete the writing process. Closing also releases
system resources and improves system performance.
Unfortunately, if some operation between file opening and file closing
throws an exception, the close operation might be skipped.
The traditional way to deal with this is to put an explicit close method
call in a separate finally block located immediately after the last
catch block, like this:
finally
{
if (fileOut != null)
{
fileOut.close();
}
} // end finally

44
That’s ugly. Starting with Java 1.7, there’s a better way.
Automatic Cleanup Using Try-With-Resources

45
All classes that implement Closeable also implement AutoCloseable.
When an object is AutoCloseable, instead of closing it with an explicit
close statement, we can ask the JVM to close it for us by opening the
AutoCloseable object with a try-with-resources header, like this:
/*************************************************************
* WriteToFile.java
* Dean & Dean
*
* This demonstrates a file writer using try-with-resources
*************************************************************/
import java.io.*;
// PrintWriter, IOException
public class WriteToFile
{
public int write(String filename, String text)
throws IOException
{
try (PrintWriter fileOut = new PrintWriter(filename))
{
fileOut.println(text);
return text.length(); // if exception is not thrown
} // end try and close fileOut automatically
} // end write
Automatic Cleanup Using Try-With-Resources
public static void main(String[] args)
{
String filename = "Feynman.txt";
String text = "It is fundamentally impossible to make "
+ "a precise prediction\n of exactly what will happen "
+ "in a given experiment.";
int length = 0;
WriteToFile writer = new WriteToFile();
try
{
length = writer.write(filename, text);
System.out.println("written string length = " + length);
}
catch (Exception e)
{
System.out.println(e.getClass());
System.out.println(e.getMessage());
}
} // end main
} // end class WriteToFile
Sample session:
written string length = 111
46