cs1081: object-oriented programming with java - handling...
TRANSCRIPT
Some Basic Ideas Putting Exceptions to Good Use
CS1081: Object-Oriented Programming with Java
Handling EXCEPTIONS
Howard Barringer
Room KB2.20/22: email: [email protected]
December 2004
Some Basic Ideas Putting Exceptions to Good Use
Supporting and Background Material
• Copies of key slides (already handed out)
• Chapter 14 of the JTL book (follows this presentation) —lecture notes
• Recommended reading books for course
• Chapter 8 (8.1 & 8.3) of Savitch “JAVA: An introduction toComputer Science and Programming”, 3rd Edition
• Chapter 13 (13.1 to 13.4) of Liang “Introduction to JAVAProgramming”, International Edition
Some Basic Ideas Putting Exceptions to Good Use
Outline
Some Basic IdeasMaking Programs more RobustLet’s play catchSeveral catchersWe can throw tooIn Summary
Putting Exceptions to Good UseExceptions in a GUIPassing the buckLooping with exceptionsSumming Up
Some Basic Ideas Putting Exceptions to Good Use
The AgeNextYear Program
public class AgeNextYear
{
public static void main(String [] args)
{
int ageNow = Integer.parseInt(args[0]);
int ageNextYear = ageNow + 1;
System.out.println("Your age now is " + ageNow);
System.out.println("Your age next year is " + ageNextYear);
}
}
Some Basic Ideas Putting Exceptions to Good Use
Running AgeNextYear
Remember what happened if we run without the argument:-
$ javac AgeNextYear.java
$ java AgeNextYear
Exception in thread "main" java.lang.ArrayIndexOutofBoundsExce
ption: 0
at AgeNextYear.main(AgeNextYear.java:5)
A run-time error occurredIndeed, an Exception was thrown by the program
Some Basic Ideas Putting Exceptions to Good Use
Fixing AgeNextYear
public class AgeNextYear
{
public static void main(String [] args)
{
if (args.length > 0)
{
int ageNow = Integer.parseInt(args[0]);
int ageNextYear = ageNow + 1;
System.out.println("Your age now is " + ageNow);
System.out.println("Your age next year is " + ageNextYear);
}
else {
System.out.println("Usage: java AgeNextYear <integer>")
}
}
}
Some Basic Ideas Putting Exceptions to Good Use
Running AgeNextYear again
$ javac AgeNextYear.java
Usage: java AgeNextYear <integer>
But suppose we now run with an incorrect argument:-
$ javac AgeNextYear.java
$ java AgeNextYear 35.375
Exception in thread "main" java.lang.NumberFormatException:
For input string: 35.375
at java.lang.NumberFormatException.forInputString(N
umberFormatException.java:48)
at java.lang.Integer.parseInt(Integer.java:477)
at java.lang.Integer.parseInt(Integer.java:518)
at AgeNextYear.main(AgeNextYear.java:7)
A different run-time error occurs
Some Basic Ideas Putting Exceptions to Good Use
Running AgeNextYear yet again
We could run with a different incorrect argument:-
$ javac AgeNextYear.java
$ java AgeNextYear Howard
Exception in thread "main" java.lang.NumberFormatException:
For input string: "Howard"
at java.lang.NumberFormatException.forInputString(N
umberFormatException.java:48)
at java.lang.Integer.parseInt(Integer.java:468)
at java.lang.Integer.parseInt(Integer.java:518)
at AgeNextYear.main(AgeNextYear.java:7)
The same run-time error – NumberFormatException – occurs
Some Basic Ideas Putting Exceptions to Good Use
Generalising ...
• It was easy to add code to detect a missing argument
• But how can we detect an incorrect argument?
• Fortunately, Java provides a mechanism for the program tocatch exceptions
Some Basic Ideas Putting Exceptions to Good Use
The AgeNextYear Program
public class AgeNextYear
{
public static void main(String [] args)
{
int ageNow = Integer.parseInt(args[0]);
int ageNextYear = ageNow + 1;
System.out.println("Your age now is " + ageNow);
System.out.println("Your age next year is " + ageNextYear);
}
}
Some Basic Ideas Putting Exceptions to Good Use
The AgeNextYearWithExceptionHandler Program
public class AgeNextYearWithExceptionHandler
{
public static void main(String [] args)
{
try
{
int ageNow = Integer.parseInt(args[0]);
int ageNextYear = ageNow + 1;
System.out.println("Your age now is " + ageNow);
System.out.println("Your age next year is " + ageNextYear);
}
catch (Exception e)
{
}
}
}
Some Basic Ideas Putting Exceptions to Good Use
Running AgeNextYearWithExceptionHandler
$ javac AgeNextYearWithExceptionHandler.java$ java AgeNextYearWithExceptionHandler$$ java AgeNextYearWithExceptionHandler 35.375$$ java AgeNextYearWithExceptionHandler Howard$
But it would be more useful to have some error message
Some Basic Ideas Putting Exceptions to Good Use
The AgeNextYearWithExceptionHandler Program
public class AgeNextYearWithExceptionHandler
{
public static void main(String [] args)
{
try {
int ageNow = Integer.parseInt(args[0]);
int ageNextYear = ageNow + 1;
System.out.println("Your age now is " + ageNow);
System.out.println("Your age next year is " + ageNextYear);
}
catch (Exception e) {
System.out.println("Usage: java AgeNextYear"
+ "WithExceptionHandler <integer>");
}
}
}
Some Basic Ideas Putting Exceptions to Good Use
Re-running AgeNextYearWithExceptionHandler
$ javac AgeNextYearWithExceptionHandler.java$ java AgeNextYearWithExceptionHandlerUsage: AgeNextYearWithExceptionHandler <integer>$$ java AgeNextYearWithExceptionHandler 35.375Usage: AgeNextYearWithExceptionHandler <integer>$$ java AgeNextYearWithExceptionHandler HowardUsage: AgeNextYearWithExceptionHandler <integer>$
But we can get more useful information
Some Basic Ideas Putting Exceptions to Good Use
Printing an exception
public class AgeNextYearWithExceptionHandler
{
public static void main(String [] args)
{
try {
int ageNow = Integer.parseInt(args[0]);
int ageNextYear = ageNow + 1;
System.out.println("Your age now is " + ageNow);
System.out.println("Your age next year is " + ageNextYear);
}
catch (Exception e) {
System.out.println("Error: " + e);
}
}
}
Some Basic Ideas Putting Exceptions to Good Use
Re-running AgeNextYearWithExceptionHandler
$ javac AgeNextYearWithExceptionHandler.java$ java AgeNextYearWithExceptionHandlerError: java.lang.ArrayIndexOutOfBoundsException: 0$$ java AgeNextYearWithExceptionHandler 35.375Error: java.lang.NumberFormatException: For inputstring: "35.375"$$ java AgeNextYearWithExceptionHandler HowardError: java.lang.NumberFormatException: For inputstring: "Howard"$
Some Basic Ideas Putting Exceptions to Good Use
Extracting just the Message
public class AgeNextYearWithExceptionHandler
{
public static void main(String [] args)
{
try {
int ageNow = Integer.parseInt(args[0]);
int ageNextYear = ageNow + 1;
System.out.println("Your age now is " + ageNow);
System.out.println("Your age next year is " + ageNextYear);
}
catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}
Some Basic Ideas Putting Exceptions to Good Use
Re-running AgeNextYearWithExceptionHandler
$ javac AgeNextYearWithExceptionHandler.java$ java AgeNextYearWithExceptionHandlerError: 0$$ java AgeNextYearWithExceptionHandler 35.375Error: For input string: "35.375"$$ java AgeNextYearWithExceptionHandler HowardError: For input string: "Howard"$
Some Basic Ideas Putting Exceptions to Good Use
Multiple catch blocks - 1
public class AgeNextYearWithThreeCatchers
{
public static void main(String [] args)
{
try
{
int ageNow = Integer.parseInt(args[0]);
int ageNextYear = ageNow + 1;
System.out.println("Your age now is " + ageNow);
System.out.println("Your age next year is " + ageNextYear);
}
Some Basic Ideas Putting Exceptions to Good Use
Multiple catch blocks - 2
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println("Error: Missing argument);
}
catch (NumberFormatException e)
{
System.out.println("Error: Argument must be an integer");
}
catch (Exception e)
{
System.out.println("Error: " + e);
}
}
}
Some Basic Ideas Putting Exceptions to Good Use
Running AgeNextYearWithThreeCatchers
$ javac AgeNextYearWithExceptionHandler.java$ java AgeNextYearWithThreeCatchersError: Missing Argument$$ java AgeNextYearWithThreeCatchers 35.375Error: Argument must be an integer$$ java AgeNextYearWithThreeCatchers HowardError: Argument must be an integer$
Some Basic Ideas Putting Exceptions to Good Use
Ordering of catch blocks
Suppose we had written AgeNextYearWrongOrder that has:-
catch (Exception e)
{
System.out.println("Error: " + e);
}
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println("Error: Missing argument");
}
catch (NumberFormatException e)
{
System.out.println("Error: Argument must be an integer");
}
In principle, the first catch block will capture all exceptionsIn fact, the program fails to compile
Some Basic Ideas Putting Exceptions to Good Use
Compiling AgeNextYearWrongOrder
$ javac AgeNextYearWithExceptionHandler.java
AgeNextYearWrongOrder.java:19: exception java.lang.ArrayIndex
OutOfBoundsException has already been caught
catch (ArrayIndexOutOfBoundsException e)
^
AgeNextYearWrongOrder.java:19: exception java.lang.NumberForm
atException has already been caught
catch (NumberFormatException e)
^
2 errors
$
Some Basic Ideas Putting Exceptions to Good Use
Catching negative inputs
public class AgeNextYearThrows
{
public static void main(String [] args)
{
try {
int ageNow = Integer.parseInt(args[0]);
int ageNextYear = ageNow + 1;
System.out.println("Your age now is " + ageNow);
System.out.println("Your age next year is " + ageNextYear);
}
catch (Exception exception) {
System.out.println("Error: " + exception);
}
}
}
Some Basic Ideas Putting Exceptions to Good Use
Catching negative inputs
public class AgeNextYearThrows
{
public static void main(String [] args)
{
try {
int ageNow = Integer.parseInt(args[0]);
if (ageNow < 0) throw new Exception("Argument " + ageNow
+ " must be a positive integer");
int ageNextYear = ageNow + 1;
System.out.println("Your age now is " + ageNow);
System.out.println("Your age next year is " + ageNextYear);
}
catch (Exception exception) {
System.out.println("Error: " + exception);
}
}
}
Some Basic Ideas Putting Exceptions to Good Use
Running AgeNextYearThrows
$ javac AgeNextYearWithExceptionHandler.java
$ java AgeNextYearThrows
Error: java.lang.ArrayIndexOutOfBoundsException: 0
$
$ java AgeNextYearThrows 35.375
Error: java.lang.NumberFormatException: For input
string: "35.375"
$
$ java AgeNextYearThrows -2
Error: Argument -2 must be positive
$
Some Basic Ideas Putting Exceptions to Good Use
System.err : An aside
It is often useful to separate error messages from “normal” output
Java provides two standard streams for output:
System.out and System.err
Thus, some exception messages may be more appropriatelyreported on System.err, e.g.
try { ... }
catch (Exception exception) {
System.err.println("Error: " + exception);
}
Some Basic Ideas Putting Exceptions to Good Use
Concepts covered so far — I
• Run-time errors are instances of the Exception class
• Exceptions can be captured by try ... catch ...statement blocks
• There can be multiple catch blocks
• Exceptions can be explicitly thrown by the throw statement
Some Basic Ideas Putting Exceptions to Good Use
Concepts covered so far — II
• The Exception class is the most general
• There are special kinds of exceptions, e.g. instances of theRuntimeException class
• Exceptions of class RuntimeException do not need to becaught, but often better if they are!
• NumberFormatException,ArrayIndexOutOfBoundsException,ArithmeticException are all special kinds of theRuntimeException class
• There are many other exception classes!
Some Basic Ideas Putting Exceptions to Good Use
Concepts covered so far — III
• Exception objects hold messages, accessed by thegetMessage() method
• Use the constructor Exception(someStringMessage)method to create an exception object carrying stringreferenced by someStringMessage.
• Exception objects have a toString() method
• Use System.err for errors when appropriate
Some Basic Ideas Putting Exceptions to Good Use
Putting Exceptions to Good Use
Examples:
• TimesTable revisited: Exceptions with a GUI
• The Date class made robust
• Using Date: a DateDifference Program
• Reading from Standard Input
Some Basic Ideas Putting Exceptions to Good Use
The TimesTable with a GUI example
The program generated a window with:
• a text field for input
• a text area for output
• a button to compute and display a times table
Let’s use it and then make it more robust
Some Basic Ideas Putting Exceptions to Good Use
The actionPerformed code
public void actionPerformed(ActionEvent event)
{
displayJTextArea.setText("");
int multiplier = Integer.parseInt(multiplierJTextField.getText());
displayJTextArea.append("--------------------------------\n");
displayJTextArea.append("| Times table for " + multiplier + "\n");
displayJTextArea.append("--------------------------------\n");
for (int thisNumber = 1; thisNumber <= 10; thisNumber++)
displayJTextArea.append("| " + thisNumber + " x " + multiplier
+ " = " + thisNumber * multiplier + "\n");
displayJTextArea.append("--------------------------------\n");
}
Some Basic Ideas Putting Exceptions to Good Use
Fixing the actionPerformed code
Our old friend
NumberFormatException
is thrown for non integer strings in the text field.
We’d better catch it and print out an error message.Where? In the text area: displayJTextArea
Some Basic Ideas Putting Exceptions to Good Use
Revised actionPerformed code
public void actionPerformed(ActionEvent event)
{
try {
displayJTextArea.setText("");
int multiplier = Integer.parseInt(multiplierJTextField.getText());
displayJTextArea.append("--------------------------------\n");
displayJTextArea.append("| Times table for " + multiplier + "\n");
displayJTextArea.append("--------------------------------\n");
for (int thisNumber = 1; thisNumber <= 10; thisNumber++)
displayJTextArea.append("| " + thisNumber + " x " + multiplier
+ " = " + thisNumber * multiplier + "\n");
displayJTextArea.append("--------------------------------\n");
} //try
catch (Exception e) {
displayJTextArea.setText("Error parsing multiplier ’"
+ multiplierJTextField.getText() + "’");
} //catch
}
Some Basic Ideas Putting Exceptions to Good Use
Running the Revised TimesTable Program
Let’s see what happens now ...
Some Basic Ideas Putting Exceptions to Good Use
Who should catch exceptions?
• So far, our exceptions have been caught by the immediateinvoking method
• either the main method• or actionPerformed method in the GUI example
• The invoking method may not be best placed to handle theexception
• So it may be better to “pass the buck”
Some Basic Ideas Putting Exceptions to Good Use
Declaring Exceptions
A method that throws an exception
either explicitly or implicitly
and doesn’t want to handle it
MUST declare it throws the exception in the method headerdefinition
Some Basic Ideas Putting Exceptions to Good Use
The original Date constructor
public Date(int requiredDay, int requiredMonth, int requiredYear)
{
year = requiredYear;
if (requiredMonth < 1) month = 1;
else if (requiredMonth > 12) month = 12;
else month = requiredMonth;
if (requiredDay < 1) day = 1;
else if (requiredDay > daysInMonth()) day = daysInMonth();
else day = requiredDay;
} // Date
Some Basic Ideas Putting Exceptions to Good Use
Let the constructor complain
public Date(int requiredDay, int requiredMonth, int requiredYear)
throws Exception
{
year = requiredYear;
month = requiredMonth;
day = requiredDay;
checkDateIsLegal();
} // Date
We design the method checkDateIsLegal to throw an exceptionfor illegal combinations
But the Date constructor passes the buck!
Some Basic Ideas Putting Exceptions to Good Use
Checking Legality of a Date
private void checkDateIsLegal() throws Exception
{
if (year < 1753)
throw new Exception("Year " + year + " must be >= 1753");
if (month < 1 || month > 12)
throw new Exception("Month " + month + " must be from 1 to 12");
if (day < 1 || day > daysInMonth())
throw new Exception("Day " + day + " must be from 1 to "
+ daysInMonth()
+ " for " + month + "/" + year);
} // Date
Some Basic Ideas Putting Exceptions to Good Use
The constructor’s invoker is now responsible
Consider the addDay method
public Date addDay()
{
int newDay = day + 1;
int newMonth = month;
int newYear = year;
if (newDay > daysInMonth()) {
newDay = 1; newMonth++;
if (newMonth > 12) {
newMonth = 1; newYear++;
} // if
} // if
// This cannot cause an exception, but Java does not know that!
try { return new Date(newDay, newMonth, newYear); }
catch (Exception exception) { return null; }
} // addDay
Some Basic Ideas Putting Exceptions to Good Use
Input Date Formats
Currently, the Date constructor takes three integer arguments for
day month year
There are many written formats, e.g.
1st December 2004 1st Dec ’041-12-2004 1/12/200401-12-04 01/12/04etc.
Let’s build a Date constructor that takes a single string assumed tobe standard UK format, i.e. 1/12/2004 or even 01/01/2005, etc.
Some Basic Ideas Putting Exceptions to Good Use
Another constructor for Date
public Date(String dateString) throws Exception
{
try {
StringTokenizer dateTokens = new StringTokenizer(dateString, "/");
String dayString = dateTokens.nextToken();
String monthString = dateTokens.nextToken();
String yearString = dateTokens.nextToken();
if (dateTokens.hasMoreTokens())
throw new Exception(); // Will be caught below.
day = Integer.parseInt(dayString);
month = Integer.parseInt(monthString);
year = Integer.parseInt(yearString);
} // try
catch (Exception exception)
{ throw new Exception("Date " + dateString
+ " not in standard UK format"); }
checkDateIsLegal();
} // Date
Some Basic Ideas Putting Exceptions to Good Use
A DateDifference Program
public class DateDifference
{
public static void main(String [] args)
{
Date date1, date2;
try {
date1 = new Date(args[0]);
date2 = new Date(args[1]);
System.out.println("From " + date1 + " to " + date2
+ " is " + date1.daysFrom(date2) + " days");
}
catch (ArrayIndexOutOfBoundsException exception)
{ System.out.println("Please supply two dates"); }
catch (Exception exception)
{ System.out.println(exception.getMessage()); }
} // main
}
Some Basic Ideas Putting Exceptions to Good Use
Reading from Standard Input
Aside from GUI input, input has been via command line arguments
Now create a DateDifferenceAgain program that prompts forinput, e.g.
$ java DateDifferenceAgain
Please type in the first date: 1/2/2004
Please type in the second date: 1/2/2oo5
Date 1/2/2oo5 not in standard UK format
Please re-type the second date: 1/2/2005
From 1/2/2004 to 1/2/2005 is 366 days
$
Some Basic Ideas Putting Exceptions to Good Use
Buffered Reader
The System class in java.lang provides
• System.in an InputStream object for standard input
• System.out an OutputStream object for standard output
• System.err an OutputStream object for standard error
Problem: Can only read byte values from an InputStream object
Solution: Use the InputStreamReader class to provide a streamof characters from a stream of byte values, and BufferedReaderclass, which has a readLine() method
BufferedReader inputReader =
new BufferedReader( new InputStreamReader( System.in ) );
Some Basic Ideas Putting Exceptions to Good Use
The revised class structure for DateDifferenceAgain
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class DateDifferenceAgain
{
private static Date inputDate(BufferedReader input, String ordinal)
{...
} // inputDate
public static void main(String [] args)
{...
} // main
} // class DateDifferenceAgain
Some Basic Ideas Putting Exceptions to Good Use
The revised main for DateDifferenceAgain
public static void main(String [] args)
{
BufferedReader in =
new BufferedReader( new InputStreamReader(System.in) );
Date date1 = inputDate(in, "first");
Date date2 = inputDate(in, "second");
int daysFrom = date1.daysFrom(date2);
System.out.println("\nFrom " + date1 + " to " + date2
+ " is " + daysFrom + " day"
+ (daysFrom!=1 ? "s" : "") );
} // main
Some Basic Ideas Putting Exceptions to Good Use
inputDate ... If it fails ... Try again ...
private static Date inputDate(BufferedReader input, String ordinal)
{
Date date = null;
System.out.print("Please type the " + ordinal + " date: ");
boolean inputValidYet = false;
do {
String inputString = null;
try { inputString = input.readLine(); } catch (Exception e) {}
try {Date
date = new Date(inputString);
inputValidYet = true; }
catch (Exception e) {
System.out.print(e.getMessage() + "\n");
+ "Please re-type the " + ordinal + " date: ");
}
} while (!inputValidYet);
return date;
} // inputDate
Some Basic Ideas Putting Exceptions to Good Use
Concepts covered today - I
• exceptions in GUIs: an unhandled exception doesn’t terminatethe main application thread
• methods declare exceptions that are not handled within themethod
• the invoking method of a method that declares an exceptionis thrown must either declare the exception or handle it
• StringTokenizer class provides methods for converting astring of characters into a sequence of tokens (strings)
Some Basic Ideas Putting Exceptions to Good Use
Concepts covered today - II
• standard input can be read using system.in• system.in gives a stream of bytes• InputStreamReader is used to convert the byte stream to a
character stream• BufferedReader provides an efficient readLine method
• the conditional expression( condition ? thenvalue : elsevalue )