When something goes wrong
Problem
12a instead of 12
The concert box office asks how many tickets you want. A ticket is €15.
import java.util.Scanner;
public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); System.out.print("How many tickets? "); int tickets = Integer.parseInt(in.nextLine()); System.out.println("Total: €" + tickets * 15); }}With the input 12 the program says "Total: €180". But the customer is in a hurry and hits one letter too many:
How many tickets? 12aException in thread "main" java.lang.NumberFormatException: For input string: "12a" at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67) at java.base/java.lang.Integer.parseInt(Integer.java:662) at java.base/java.lang.Integer.parseInt(Integer.java:778) at Main.main(Main.java:7)The program stops. Instead of the total you get a long text, and the "Total" line is never reached. What does this text mean, and how do you keep the program from stopping because of one letter?
The idea
Exception
Integer.parseInt has to return a number. It cannot make a number out of "12a", and returning just any number would be a lie. So the method creates an object that describes the problem and throws it. This object is called an exception.
If nobody catches it, the program stops and shows what happened. This text is called a stack trace, and you read it from top to bottom:
- The first line: the kind of exception and the message.
NumberFormatException: For input string: "12a": the number has the wrong format, and here is the text. - The lines with "at": where it happened. This is the call stack from the recursion lesson, from the inside out. At the top is the method that threw the exception, and at the bottom is
main. - Your line: the first "at" line that points to your file. Here:
Main.java:7. The lines above it are in the code of Java, and you rarely need them.
The messages are in English because the language writes them, not you.
Try it
Find the bug
The program should print the last score from the test. It stops with this:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5 at Main.lastScore(Main.java:3) at Main.main(Main.java:8)Read the stack trace. Which line of the program is the bug on?
Pick the line with the bug.
lastScore, line 3. The line under it is main, line 8: the method was called from there, but the bug is not there. The first line also says what it is: index 5 with length 5. The last index is scores.length - 1.The idea
try and catch
An exception can be caught. The code that might throw goes in try. What should happen on an error, you write in catch:
Scanner in = new Scanner(System.in);System.out.print("How many tickets? ");try { int tickets = Integer.parseInt(in.nextLine()); System.out.println("Total: €" + tickets * 15);} catch (NumberFormatException e) { System.out.println("That is not a whole number.");}System.out.println("Thank you!");12aHow many tickets? That is not a whole number.Thank you!- In the parentheses of
catchyou write which kind of exception you catch. - If everything in
trygoes well,catchis skipped. With the input12the output is "Total: €180" and "Thank you!". - After
catchthe program continues on. It no longer stops.
Scanner in = new Scanner(System.in);System.out.print("How many tickets? ");try { int tickets = Integer.parseInt(in.nextLine()); System.out.println("Total: €" + tickets * 15);} catch (NumberFormatException e) { System.out.println("That is not a whole number.");}System.out.println("Thank you!");Step by step, in words
- The program asks how many tickets. You type "12a".
- In try: parseInt tries to make a number out of "12a".
- It cannot. The method throws NumberFormatException.
- Execution jumps straight into catch. The "Total" line is skipped.
- catch is for exactly this kind of error: it prints the message for the customer.
- After catch the program continues. It does not stop.
More precisely, if you are curious
- A variable declared in
try(heretickets) exists only until the closing brace oftry. If you need it after the block too, declare it beforetry.
- In Java the name is required:
catch (NumberFormatException e), even if you do not use it. By habit it ise.
Try it
Which lines run?
What will the program print?
System.out.println("Start");try { System.out.println("Reading"); int seats = Integer.parseInt("ten"); System.out.println("Got " + seats);} catch (NumberFormatException e) { System.out.println("Bad input");}System.out.println("End");"Reading" is printed because it comes before the error. Then parseInt throws, and execution jumps straight into catch. "Got" is skipped. After catch the program continues with "End".
The idea
What the exception says
The exception is an object, and e in catch (NumberFormatException e) is the variable that points to it. The object has a way to give you the message: e.getMessage().
Scanner in = new Scanner(System.in);try { int tickets = Integer.parseInt(in.nextLine()); System.out.println("Total: €" + tickets * 15);} catch (NumberFormatException e) { System.out.println("Error: " + e.getMessage());}12aError: For input string: "12a"The message is useful to you when you are looking for the bug. To the customer you usually show your own text, in their language, such as "That is not a whole number."
The idea
Ask until it is right
The customer made a mistake. You do not have to stop. Ask again:
Scanner in = new Scanner(System.in);int tickets;while (true) { System.out.print("How many tickets? "); try { tickets = Integer.parseInt(in.nextLine()); break; } catch (NumberFormatException e) { System.out.println("Enter a whole number, for example 3."); }}System.out.println("Total: €" + tickets * 15);12atwelve12How many tickets? Enter a whole number, for example 3.How many tickets? Enter a whole number, for example 3.How many tickets? Total: €180while (true) keeps looping until break stops it. And break is reached only if parseInt succeeds. On an error, execution jumps into catch, and after it the loop starts over and asks again.
More precisely, if you are curious
- Java has no
TryParse. CatchingNumberFormatExceptionis the usual way.Scanneralso hashasNextInt(), which checks whether a whole number comes next, without reading it.
Try it
Division by zero
Not every exception comes from the input. The average order value is the revenue divided by the number of orders. But on the first day there are no orders. With whole numbers, revenue / orders stops the program:
Exception in thread "main" java.lang.ArithmeticException: / by zeroWhat about decimal numbers?
What will the program print?
int revenue = 90;int orders = 0;double average = (double) revenue / orders;System.out.println(average);NaN ("not a number"). The program goes on with a wrong value without saying anything. So before such a division, check the divisor with if.More precisely, if you are curious
- Whole numbers have no value for infinity, so all that is left there is the exception. For decimal numbers, the rules come from the IEEE 754 standard for floating point numbers, which every language follows.
Exercise
Programs that do not stop
Solve these in your editor. Do not print prompts, so your output matches the sample.
1. Text to number. Write a method toNumber(String text) that returns the text as a double. Read two lines. For each, print the number or a message that it is not a number. Catch the exception in main.
123.45123.abcThe number is: 123.45Error: 123.abc is not a number.2. No such product. The prices are in the array { 120, 75, 300, 45, 89 }. Read a product number from 1 to 5 and print its price. If the number is outside the array, catch the exception and say so. With the input 2 the output is Price: €75.
7There is no product number 7.3. Average order. Read the revenue and the number of orders (whole numbers) and print the average with integer division. If there are no orders, catch the exception. With the input 900 and 4 the output is Average: €225.
900No orders yet.More tasks
- Age, until it is right. Ask for an age until the input is a whole number from 0 to 120. For text like "twenty" say "That is not a number.", and for 150 say "The age must be from 0 to 120." One is an exception, the other is an
if. - Sum of the valid ones. Read 5 lines. Add up the ones that are whole numbers and count the skipped ones: with the input
10,abc,5,7.5,3the output isSum: 18, skipped: 2. - Division on demand. Read two whole numbers and print the quotient. On text instead of a number and on division by zero, the program does not stop. It asks again for both numbers.
Test