Exceptions
An Introduction with Examples in Java |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
/** * A simple PriceCalculator * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class PriceCalculator { private double[] discounts = {0., 1., 5., 20., 100.}; /** * Apply a quantity-based discount */ public double applyDiscount(double price, int quantity) { double discountedPrice; int i; discountedPrice = price; // Find the discount and adjust the price; i = discounts.length - 1; while (i >= 0) { if (quantity >= i) { discountedPrice = price - discounts[i]; break; } --i; } // Return -1 to indicate a problem if (discountedPrice < 0) discountedPrice = -1; return discountedPrice; } }
String
into a double
?
double
values are legitimate return values
there is no special value that can be used.return
statement (with or without a value
to return)throw
statement
(which must pass back a Throwable
object)Throwable
should
be used)void
)throws
clause (that specifies the exception)
In the following example, a throw
statement is used
for an alternative return and the return
statement
is used for a normal return
.
/** * Return the host portion of an email address * * @param address A String representation of an email address * @return The host portion of the email address */ public static String getHost(String address) throws IllegalArgumentException { int index; index = address.indexOf("@"); if ((index <= 0) || (index >= address.length()-1)) { throw new IllegalArgumentException("Misplaced or missing @."); } else { return address.substring(index+1); } // Note: This is equivalent to: // // if ((index <= 0) || (index >= address.length()-1)) // { // throw new IllegalArgumentException("Misplaced or missing @."); // } // // return address.substring(index+1); }
try
block is used to indicate the code that
should be executed when the method returns normallycatch
block (which is passed a
Throwable
) is used to indicate the code that
should be executed when the method returns "abnormally"try-catch
Statementtry-catch
Statement (cont.)
/** * A simple application that prints the host associated with an * email address * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class EmailProcessor { /** * The entry-point of the application * * @param args The command-line arguments */ public static void main(String[] args) { String host; if ((args != null) && (args.length > 0)) { try { host = EmailAddress.getHost(args[0]); System.out.println("Host: "+host); } catch (IllegalArgumentException iae) { System.out.println("Try another email address!"); } } } }
try-catch
Statement (cont.)
/** * A simple application that prompts the processes * grades (provided as command line arguments) * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class GradeProcessor { /** * The entry-point of the application * * @param grades The space-delimited grades */ public static void main(String[] grades) { double total; final double DEFAULT = 50.0; total = 0; for (int i=0; i<grades.length; i++) { try { total += Double.parseDouble(grades[i]); } catch (NumberFormatException nfe) { total += DEFAULT; } } System.out.printf("Average: %3.1f", total/grades.length); } }
catch
Portion
catch(NumberFormatException nfe) { total += DEFAULT; }
NumberFormatException
object that this statement
is going to "catch" and name nfe
.
try-catch
throws
clause in the declaration)
/** * A simple utility class for integer division that * illustrates how exceptions can be re-thrown * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class Divider { /** * Performs integer division * * @param num The numerator * @param denom The denominator * @return The result of the integer division * @throws An ArithmeticException in case of a divide by zero */ public int divide(int num, int denom) throws ArithmeticException { int result; result = num / denom; return result; } }
/** * A simple application that sequentially divides * elements in an array. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class DividerDriver { public static void main(String[] args) { Divider d; int i, ratio; int[] numbers = {100,10,0,5,2,8,0,30}; d = new Divider(); for (i=0; i < numbers.length-1; i++) { try { ratio = d.divide(numbers[i], numbers[i+1]); System.out.println(numbers[i]+"/"+numbers[i+1]+"="+ratio); } catch (ArithmeticException ae) { System.out.println("Couldn't calculate "+ numbers[i]+"/"+numbers[i+1]); } } } }
Catch
Blockscatch
Block:
catch
blocks (but it is better to
use a logger)main()
?
catch
blocks that are in
the main()
method
Beginning programmers often make the following mistake
when told that a method must throw an exception. For example, if
they are told that the method they are writing must throw a
NumberFormatException
they add a throws
NumberFormatException
clause to the method declaration and do
the following.
try { d = Double.parseDouble(s); } catch (NumberFormatException nfe) { throw new NumberFormatException(); // throw nfe; is also bad }
Instead you should add a throws NumberFormatException
clause to the method declaration and do the following.
d = Double.parseDouble(s);
The only reason to include a throw
statement in
a catch
block is if a different exception must be
thrown.
The following two fragments have very different behavior.
for (i=0; i < args.length; i++) { try { value = Double.parseDouble(args[i]); System.out.println(args[i]+" is a number"); } catch (NumberFormatException nfe) { System.out.println("Not a number"); } }
try { for (i=0; i < args.length; i++) { value = Double.parseDouble(args[i]); System.out.println(args[i]+" is a number"); } } catch (NumberFormatException nfe) { System.out.println("Not a number"); }
/** * A simple calculator. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class Calculator { /** * The entry point of the application. * * @param args Syntax: left-operand operator right operand */ public static void main(String[] args) { char operator; int left, result, right; try { result = 0; left = Integer.parseInt(args[0]); right = Integer.parseInt(args[2]); operator = args[1].charAt(0); switch (operator) { case '+': result = left + right; break; case '-': result = left - right; break; case '/': result = left / right; break; } System.out.println("Result: "+result); } catch (ArithmeticException ae) { System.out.println("Does not compute!"); } catch (NumberFormatException nfe) { System.out.println("Non-numeric operand!"); } } }
try-catch
BlocksparseInt
generated the exception in the previous
example?try-catch
statements
try-catch
Blocks (cont.)
/** * A simple calculator. * * This version illustrates how try-catch blocks can be nested. * * @author Prof. David Bernstein, James Madison University * @version 2.0 */ public class CalculatorNested { /** * The entry point of the application. * * @param args Syntax: left-operand operator right operand */ public static void main(String[] args) { char operator; int left, result, right; result = 0; try { left = Integer.parseInt(args[0]); try { right = Integer.parseInt(args[2]); operator = args[1].charAt(0); try { switch (operator) { case '+': result = left + right; break; case '-': result = left - right; break; case '/': result = left / right; break; } System.out.println("Result: "+result); } catch (ArithmeticException ae) { System.out.println("Does not compute!"); } } catch (NumberFormatException rnfe) { System.out.println("Right-side operand is non-numeric!"); } } catch (NumberFormatException lnfe) { System.out.println("Left-side operand is non-numeric!"); } } }
ArrayIndexOutOfBoundsException
so I'll fix it by
adding a try
-catch
block.")throws
clause in its declaration{exceptions=name[,...]}
after the return type of the method to indicate that
exceptions are thrown