Different errors and finally
Problem
The bill for zero people
The program splits the restaurant bill evenly. You already know about wrong input, and you catch it:
import java.util.Scanner;
public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); try { int bill = Integer.parseInt(in.nextLine()); int people = Integer.parseInt(in.nextLine()); System.out.println("Per person: €" + bill / people); } catch (NumberFormatException e) { System.out.println("Enter whole numbers."); } }}The bill is €90, and someone enters 0 for the number of people. The program stops again:
Exception in thread "main" java.lang.ArithmeticException: / by zero at Main.main(Main.java:9)catch is there, but it does not help. It catches only NumberFormatException, and this is a different kind of exception.
The idea
Several catch blocks
One try can be followed by several catch blocks, one for each kind of error:
Scanner in = new Scanner(System.in);try { int bill = Integer.parseInt(in.nextLine()); int people = Integer.parseInt(in.nextLine()); System.out.println("Per person: €" + bill / people);} catch (NumberFormatException e) { System.out.println("Enter whole numbers.");} catch (ArithmeticException e) { System.out.println("People cannot be 0.");}| Input | Output |
|---|---|
| 90 and 3 | Per person: €30 |
| 90 and 0 | People cannot be 0. |
| 90 and three | Enter whole numbers. |
The blocks are checked from top to bottom. The first one whose kind matches runs, and only that one. The rest are skipped.
Scanner in = new Scanner(System.in);try { int bill = Integer.parseInt(in.nextLine()); int people = Integer.parseInt(in.nextLine()); System.out.println("Per person: €" + bill / people);} catch (NumberFormatException e) { System.out.println("Enter whole numbers.");} catch (ArithmeticException e) { System.out.println("People cannot be 0.");}Step by step, in words
- First run: the bill is €90, and there are 0 people.
- 90 / 0 is division by zero: ArithmeticException is thrown.
- The first catch is for NumberFormatException. It does not match, so it is skipped.
- The second one matches. It runs, and only it.
- Second run: "three" is typed in instead of the number of people.
- parseInt cannot make a number: NumberFormatException is thrown.
- The very first catch matches. The second is never reached.
- The blocks are checked from top to bottom. The first one that matches runs.
More precisely, if you are curious
- When two kinds are handled the same way, you can catch them in one block with
|:catch (NumberFormatException | ArithmeticException e).
The idea
The family of exceptions
Exceptions are classes, related by inheritance. Here are the most common ones:
Throwable├── Error a serious JVM problem, not caught│ ├── OutOfMemoryError│ └── StackOverflowError└── Exception ├── IOException files, network ├── InterruptedException └── RuntimeException ├── ArithmeticException ├── IllegalArgumentException │ └── NumberFormatException ├── IndexOutOfBoundsException │ └── ArrayIndexOutOfBoundsException └── NullPointerExceptioncatch catches the kind you name and all of its subclasses. This is the polymorphism from the module on inheritance: a variable of a parent type can point to an object of a subclass. That is why catch (Exception e) catches everything:
static void split(String billText, String peopleText) { try { int bill = Integer.parseInt(billText); int people = Integer.parseInt(peopleText); System.out.println("Per person: €" + bill / people); } catch (Exception e) { System.out.println(e.getClass().getSimpleName() + ": " + e.getMessage()); }}
split("90", "3");split("90", "0");split("90", "three");Per person: €30ArithmeticException: / by zeroNumberFormatException: For input string: "three"e.getClass().getSimpleName() gives the name of the object's real class. The variable is of type Exception, but the object is of a subclass.
More precisely, if you are curious
Errorand its subclasses are problems of the JVM itself, such as running out of memory or a stack overflow. The JVM itself throws them. The program usually cannot do anything sensible after them, so they are not caught.catch (Throwable t)would catch them too. Do not do it.
- Catch as specifically as you can. catch (Exception e) also hides errors you did not expect, such as a bug in your own code.
Try it
Which catch?
What will the program print?
int[] seats = { 4, 2, 6 };try { System.out.println("Table 4: " + seats[3] + " seats");} catch (ArithmeticException e) { System.out.println("Division by zero");} catch (RuntimeException e) { System.out.println("Runtime error");} catch (Exception e) { System.out.println("General error");}seats[3] is outside the array of 3 elements. ArrayIndexOutOfBoundsException is not an ArithmeticException, so the first block is skipped. It is a subclass of RuntimeException, so the second one matches and runs. The third is never reached, even though it would match too.Try it
Find the bug
Pick the line with the bug.
Exception is the parent of ArithmeticException. The first catch catches everything, and the second can never be reached. The compiler says so: "exception ArithmeticException has already been caught". The rule is from the specific to the general: swap the two blocks.The idea
Checked exceptions and throws
The compiler makes you handle some exceptions. Here is a method that prints a receipt and waits for the printer with Thread.sleep (a pause in milliseconds):
static void printReceipt() { System.out.println("Printing the receipt..."); Thread.sleep(300); System.out.println("Done.");}The program does not compile: "unreported exception InterruptedException; must be caught or declared to be thrown". The pause can be interrupted from outside, and the compiler wants you to say what should happen then. You have two choices: catch the exception in the method, or declare it with throws and let the caller decide:
static void printReceipt() throws InterruptedException { System.out.println("Printing the receipt..."); Thread.sleep(300); System.out.println("Done.");}
try { printReceipt();} catch (InterruptedException e) { System.out.println("Interrupted.");}Printing the receipt...Done.- Checked exceptions are the subclasses of
Exceptionthat are notRuntimeException. They usually come from outside, from things beyond the program: a missing file, a dropped network connection, an interrupted pause. - Unchecked exceptions are
RuntimeExceptionand its subclasses. They are usually a mistake in the code or in the input: an index, division by zero, a wrong format. The compiler does not make you catch them.
You will meet a checked exception most often with files: IOException.
The idea
finally: always
The till's printer has to be freed after every receipt, or the next one will not print. Whatever happens, success or error:
static void printReceipt(String qtyText) { System.out.println("The printer is busy."); try { int qty = Integer.parseInt(qtyText); System.out.println("Coffee: " + qty + " x €2 = €" + qty * 2); } catch (NumberFormatException e) { System.out.println("Wrong quantity: " + qtyText); } finally { System.out.println("The printer is free."); }}
printReceipt("3");printReceipt("three");The printer is busy.Coffee: 3 x €2 = €6The printer is free.The printer is busy.Wrong quantity: threeThe printer is free.finally runs always: when everything goes well, when the exception is caught, and even when none of these catch blocks catches it. In that last case finally runs, and the exception keeps going up to the next try that can catch it.
Why not just a line after the try/catch? Because that line is skipped if the exception is of a kind that catch does not catch, or if try has a return. finally is not skipped.
More precisely, if you are curious
- You can also have
trywithfinallyand nocatch: the exception is not caught, but the cleanup runs.
- For resources that have to be closed, such as files and database connections, there is a shorter form:
try (...)with the resource in the parentheses (try-with-resources). We will see it when we get to files. It closes the resource by itself, just asfinallywould.
finallydoes not run only if the whole program is stopped, for example withSystem.exit(0).
Try it
finally and return
What will the program print?
static String reserve(int guests) { try { if (guests > 6) { return "No table for " + guests; } return "Table for " + guests; } finally { System.out.println("Logged the attempt."); }}
System.out.println(reserve(8));A return in try does not skip finally. The method runs finally first, then returns the text. That is why "Logged the attempt." is printed before the result, which println gets only when the method is done.
Exercise
Programs with several catch blocks
Solve these in your editor. Do not print prompts.
1. Price per person. The prices are in the array { 120, 75, 300 }. Read a product number (from 1) and the number of people, and print the price per person with integer division. Catch three things: a product that does not exist, zero people, and everything else (then print the exception's message).
| Input | Output |
|---|---|
| 2 and 3 | Per person: €25 |
| 5 and 2 | No such product. |
| 1 and 0 | The number of people cannot be 0. |
| 1 and two | Error: For input string: "two" |
2. Calculator. Read a whole number, an operator (+, -, * or /) and a second whole number, each on its own line. Print the calculation. On text instead of a number say "Enter whole numbers.", on division by zero "Division by zero.", and on any other operator "Unknown operator: " followed by the operator.
84/484 / 4 = 2184/0Division by zero.More tasks
- The card terminal. The method
pay(String amountText)prints "The terminal is connected.", then "Paid €X" or a message about wrong input, and infinally"The terminal is off." Call it with "25" and with "twenty". - Average grade without crashes. Read 5 lines with grades. Skip the ones that are not whole numbers (an exception) or are not from 2 to 6 (an
if). Print the average with integer division. If no grade is valid, the division by zero throws: catch it and print "No valid grades." - Predict and check. Write a
trywithcatchandfinally, where each block prints a line. Predict the output on success, on a caught exception and on an uncaught one, then run it.
- Pause. Write a method
pause(int ms)that callsThread.sleep(ms). Do it once withthrows InterruptedExceptionand once withtry/catchinside. What doesmainhave to do in each of the two cases?
Test