Chapter 14
Exception Handling
Chapter Goals
- To learn how to throw exceptions
- To be able to design your own exception classes
- To understand the difference between checked and unchecked exceptions
- To learn how to catch exceptions
- To know when and where to catch an exception
Error Codes
- Traditional approach to error handling: method returns error code
- Example: JOptionPane.showInputDialog returns null
if user hits Cancel
- Problem: Calling method may forget to check for error code
- Problem: Calling method may not know how to fix error--then it needs
to return an error code
- Symptom: Programming for success
x.doSomething()
is replaced by programming for failure
if (!x.doSomething()) return false;
Exceptions
- Can't be overlooked
- Can be handled by a competent handler, not necessarily the
calling method
- Throw an exception object to indicate failure
if (failure)
{
XxxException e = new XxxException(. . .);
throw e;
}
- More concisely
throw new XxxException(. . .);
Exceptions
public class BankAccount
{
public void withdraw(double amount)
{
if (amount > balance)
throw new IllegalArgumentException(
"Amount exceeds balance");
balance = balance - amount;
}
...
}
Hierarchy of Exception Classes
Syntax 14.1: Throwing an Exception
Example:
|
throw new IllegalArgumentException();
|
Purpose:
To throw an exception and transfer control to a handler for this exception
type |
Checked Exceptions
- Compiler checks that you are aware of the exception
- Generally used for errors that can happen even in correct programs
- IOException and its sublcasses are checked exceptions
- NullPointerException, ArrayIndexOutOfBoundsException
, . . . are unchecked--they are your fault :-)
- Virtual machine errors (e.g. OutOfMemoryError) are unchecked
- Classification not perfect. For example, Integer.parseInt
throws unchecked NumberFormatException
- Checked exceptions are subclasses of Exception that are
not subclasses of RuntimeException
Checked and Unchecked Exceptions
Exception Specifications
- BufferedReader.readLine may throw IOException
- Tag calling method with throws IOException
public class Coin
{
public void read(BufferedReader in) throws IOException
{
value = Double.parseDouble(in.readLine());
name =in.readLine();
}
...
}
Exception Specifications
- Need to tag caller of Coin.read as well
- Stop at main or with handler (see below)
- Can have multiple exception types
public void read()
throws IOException, ClassNotFoundException
- throws specifier not a sign of irresponsible programming
- Better to declare exception than to handle it incompetently
Syntax 14.2: Exception Specification
|
accessSpecifier returnType methodName(parameterType
parameterName, . . .)
throws ExceptionClass, ExceptionClass . . . |
Example:
|
public void read(BufferedReader in) throws IOException |
Purpose:
To indicate the checked exceptions that a method can throw |
Designing Your Own Exception Types
- if (amount > balance)
throw new InsufficientFundsException(. . .);
- Make it an unchecked exception--programmer could have avoided it by
calling getBalance first
- Extend RuntimeException
- Supply two constructors
Designing Your Own Exception Types
public class InsufficientFundsException
extends RuntimeException
{
public InsufficientFundsException()
{
}
public InsufficientFundsException(String reason)
{
super(reason);
}
}
Catching Exceptions
try
{
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
System.out.println("How old are you?");
String inputLine = in.readLine();
int age = Integer.parseInt(inputLine);
age++;
System.out.println("Next year,you'll be " + age);
}
catch (IOException exception)
{
System.out.println("Input/output error " +exception);
}
catch (NumberFormatException exception)
{
System.out.println("Input was not a number");
}
Catching Exceptions
- Statements in try block are executed
- If no exceptions occur, catch clauses are skipped
- If exception of matching type occurs, execution jumps to catch
clause
- If exception of another type occurs, it is thrown to the calling method
- If main doesn't catch an exception, the program terminates
with a stack trace
Syntax 14.3: General Try Block
|
try { statement statement ... } catch (ExceptionClass exceptionObject) { statement statement ... } catch (ExceptionClass exceptionObject) { statement statement ... } ...
|
Example:
|
try { System.out.println("What is your name?"); String name = console.readLine(); System.out.println("Hello,"+name +"!"); } catch (IOException exception) { exception.printStackTrace(); System.exit(1); }
|
Purpose:
To execute one or more statements that may generate exceptions. If
an exception of a particular type occurs, then stop executing those statements
and instead go to the matching catch clause. If no exception occurs, then
skip the catch clauses.
|
The finally Clause
- Exception terminates current method
- Danger: Can skip over essential code
- Example:
BufferedReader in;
in = new BufferedReader(
new FileReader(filename));
purse.read(in);
in.close();
- Must execute in.close() even if exception happens
- Use finally clause for code that must be executed "no matter
what"
The finally Clause
BufferedReader in = null;
try
{
in = new BufferedReader(
new FileReader(filename));
purse.read(in);
}
finally
{
if (in !=null) in.close();
}
The finally Clause
- Executed when try block comes to normal end
- Executed if a statement in try block throws an exception,
before exception is thrown out of try block
- Can also be combined with catch clauses
Syntax 14.4: The finally Clause
|
try
{
statement
statement
...
}
finally
{
statement
statement
...
}
|
Example:
|
BufferedReader in = null; try { in = new BufferedReader( new FileReader(filename)); purse.read(in); } finally { if (in !=null) in.close(); }
|
Purpose:
To execute one or more statements that may generate exceptions, and
to execute the statements in the finally clause whether or not an
exception occured. |
A Complete Example
- Program
- reads coin descriptions from file
- adds coins to purse
- prints total
- What can go wrong?
- File might not exist
- File might have data in wrong format
- Who can detect the faults?
- main method of PurseTest interacts with user
- main method can report errors
- Other methods pass exceptions to caller
The read method of the Coin class
- Distinguishes between expected and unexpected end of
file
public boolean read(BufferedReader in) throws IOException
{
String input =in.readLine();
if (input == null) // normal end of file
return false;
value = Double.parseDouble(input);
// may throw unchecked NumberFormatException
name = in.readLine();
if (name == null) // unexpected end of file
throw new EOFException("Coin name expected");
return true;
}
The read method of the Purse class
- Unconcerned with exceptions
- Just passes them to caller
public void read(BufferedReader in)
throws IOException
{
boolean done = false;
while (!done)
{
Coin c = new Coin();
if (c.read(in)) add(c);
else done =true;
}
}
The readFile method of the Purse class
- finally clause closes files if exception happens
public void readFile(String filename)
throws IOException
{
BufferedReader in = null;
try
{
in = new BufferedReader(
new FileReader(filename));
read(in);
}
finally
{
if (in != null)
in.close();
}
}
User interaction in main
- If an exception occurs, user can specify another file name
boolean done = false;
String filename = JOptionPane.showInputDialog("Enter file name");
while (!done)
{
try
{
Purse myPurse = new Purse();
myPurse.readFile(filename);
System.out.println("total=" + myPurse.getTotal());
done =true;
}
catch (IOException exception)
{
System.out.println("Input/output error " + exception);
}
catch (NumberFormatException exception)
{
exception.printStackTrace(); // error in file format
}
if (!done)
{
filename = JOptionPane.showInputDialog( "Try another file:");
if (filename == null)
done =true;
}
}
Scenario
- PurseTest.main calls Purse.readFile
- Purse.readFile calls Purse.read
- Purse.read calls Coin.read
- Coin.read throws an EOFException
- Coin.read has no handler for the exception and terminates
immediately.
- Purse.read has no handler for the exception and terminates
immediately
- Purse.readFile has no handler for the exception and terminates
immediately after executing the finally clause and closing the file.
- PurseTest.main has a handler for an IOException ,
a superclass of EOFException. That handler prints a message to the
user. Afterwards, the user is given another chance to enter a file name.
Note that the statement printing the purse total has been skipped.
File PurseTest.java
1 | import javax.swing.JOptionPane; |
2 | import java.io.IOException; |
3 | |
4 | /** |
5 | This program prompts the user to enter a file name |
6 | with coin values. A purse object is filled with |
7 | the coins specified in the file. In case of an exception, |
8 | the user can choose another file. |
9 | */ |
10 | public class PurseTest |
11 | { |
12 | public static void main(String[] args) |
13 | { |
14 | boolean done = false; |
15 | String filename |
16 | = JOptionPane.showInputDialog("Enter file name"); |
17 | |
18 | while (!done) |
19 | { |
20 | try |
21 | { |
22 | Purse myPurse = new Purse(); |
23 | myPurse.readFile(filename); |
24 | System.out.println("total=" + myPurse.getTotal()); |
25 | done = true; |
26 | } |
27 | catch (IOException exception) |
28 | { |
29 | System.out.println("Input/output error " + exception); |
30 | } |
31 | catch (NumberFormatException exception) |
32 | { |
33 | exception.printStackTrace(); |
34 | } |
35 | |
36 | if (!done) |
37 | { |
38 | filename = JOptionPane.showInputDialog( |
39 | "Try another file:"); |
40 | if (filename == null) done = true; |
41 | } |
42 | } |
43 | System.exit(0); |
44 | } |
45 | } |
File Purse.java
1 | import java.io.BufferedReader; |
2 | import java.io.FileReader; |
3 | import java.io.IOException; |
4 | |
5 | /** |
6 | A purse computes the total of a collection of coins. |
7 | */ |
8 | public class Purse |
9 | { |
10 | /** |
11 | Constructs an empty purse. |
12 | */ |
13 | public Purse() |
14 | { |
15 | total = 0; |
16 | } |
17 | |
18 | /** |
19 | Read a file with coin descriptions and adds the coins |
20 | to the purse. |
21 | @param filename the name of the file |
22 | */ |
23 | public void readFile(String filename) |
24 | throws IOException |
25 | { |
26 | BufferedReader in = null; |
27 | try |
28 | { |
29 | in = new BufferedReader(new FileReader(filename)); |
30 | read(in); |
31 | } |
32 | finally |
33 | { |
34 | if (in != null) in.close(); |
35 | } |
36 | } |
37 | |
38 | /** |
39 | Read a file with coin descriptions and adds the coins |
40 | to the purse. |
41 | @param in the buffered reader for reading the input |
42 | */ |
43 | public void read(BufferedReader in) |
44 | throws IOException |
45 | { |
46 | boolean done = false; |
47 | while (!done) |
48 | { |
49 | Coin c = new Coin(); |
50 | if (c.read(in)) |
51 | add(c); |
52 | else |
53 | done = true; |
54 | } |
55 | } |
56 | |
57 | /** |
58 | Add a coin to the purse. |
59 | @param aCoin the coin to add |
60 | */ |
61 | public void add(Coin aCoin) |
62 | { |
63 | total = total + aCoin.getValue(); |
64 | } |
65 | |
66 | /** |
67 | Get the total value of the coins in the purse. |
68 | @return the sum of all coin values |
69 | */ |
70 | public double getTotal() |
71 | { |
72 | return total; |
73 | } |
74 | |
75 | private double total; |
76 | } |
77 | |
File Coin.java
1 | import java.io.BufferedReader; |
2 | import java.io.EOFException; |
3 | import java.io.IOException; |
4 | |
5 | /** |
6 | A coin with a monetary value. |
7 | */ |
8 | public class Coin |
9 | { |
10 | /** |
11 | Constructs a default coin. |
12 | Use the read method to fill in the value and name. |
13 | */ |
14 | public Coin() |
15 | { |
16 | value = 0; |
17 | name = ""; |
18 | } |
19 | |
20 | /** |
21 | Constructs a coin. |
22 | @param aValue the monetary value of the coin. |
23 | @param aName the name of the coin |
24 | */ |
25 | public Coin(double aValue, String aName) |
26 | { |
27 | value = aValue; |
28 | name = aName; |
29 | } |
30 | |
31 | /** |
32 | Reads a coin value and name. |
33 | @param in the reader |
34 | @return true if the data was read, |
35 | false if the end of the stream was reached |
36 | */ |
37 | public boolean read(BufferedReader in) |
38 | throws IOException |
39 | { |
40 | String input = in.readLine(); |
41 | if (input == null) return false; |
42 | value = Double.parseDouble(input); |
43 | name = in.readLine(); |
44 | if (name == null) |
45 | throw new EOFException("Coin name expected"); |
46 | return true; |
47 | } |
48 | |
49 | /** |
50 | Gets the coin value. |
51 | @return the value |
52 | */ |
53 | public double getValue() |
54 | { |
55 | return value; |
56 | } |
57 | |
58 | /** |
59 | Gets the coin name. |
60 | @return the name |
61 | */ |
62 | public String getName() |
63 | { |
64 | return name; |
65 | } |
66 | |
67 | private double value; |
68 | private String name; |
69 | } |
70 | |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | |
77 | |
78 | |