cs1081: object-oriented programming with java - handling...

54
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

Upload: others

Post on 20-Jun-2020

9 views

Category:

Documents


0 download

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

Running DateDifference

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 )

Some Basic Ideas Putting Exceptions to Good Use

That’s nearly all there’s time for in CS1081

LAST LECTURE — Tomorrow, Friday 17th December 2004

Closing Session about the Examination