{"id":"exceptions-catching","slug":"try-and-catch","title":"Catching an exception: try and catch","text":"You've met exceptions since module 12: a program gets to a line it can't carry out, and it stops with a report. Module 20 put file reading in a `try` block with a `catch` block after it. This module is all about exceptions: what they are, how to catch the ones you expect, which ones Java makes you plan for, and how your own classes can throw one.\n\n### A crash, read slowly\n\nThis program asks for a height in centimeters and prints it in meters. The user typed a unit after the number:\n\n```java\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        System.out.println(\"Height in centimeters?\");\n        int height = Integer.parseInt(scanner.nextLine());\n        System.out.println(\"That's \" + (height / 100.0) + \" meters\");\n    }\n}\n```\n\n```input\n180 cm\n```\n\n```output\nHeight in centimeters?\n```\n\n```crash\nException in thread \"main\" java.lang.NumberFormatException: For input string: \"180 cm\"\n\tat Main.main(Main.java:7)\n```\n\n`Integer.parseInt` can't turn `180 cm` into a number, and it can't return a made-up number either. So it **throws an exception**: it creates an object that describes the problem, and hands that object to the code that called it, in place of a return value. Nothing here catches it, so it goes on out of `main`, and Java stops the program and prints the report:\n\n- `java.lang.NumberFormatException` is the exception's class, with its package in front (`java.lang` is the package of `String` and `Integer`). Exceptions are objects, and each kind of problem has a class of its own.\n- `For input string: \"180 cm\"` is the exception's **message**, written by the code that threw it.\n- `at Main.main(Main.java:7)` is the **stack trace**: where the program was when it happened. Line 7 is the `parseInt` line. On your own computer, Java also prints a few `at` lines from inside its own classes above this one (from inside `Integer.parseInt`, where the text was checked). The line to look for is the first one from your own code.\n\nEverything after line 7 was skipped: the meters were never printed.\n\n### try and catch\n\nA program that reads what people type should expect text that isn't a number. `try` and `catch` let it decide what happens then:\n\n```java\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        System.out.println(\"Height in centimeters?\");\n        String line = scanner.nextLine();\n        try {\n            int height = Integer.parseInt(line);\n            System.out.println(\"That's \" + (height / 100.0) + \" meters\");\n        } catch (NumberFormatException e) {\n            System.out.println(\"Please type just the number, like 172\");\n        }\n        System.out.println(\"Thanks!\");\n    }\n}\n```\n\n```input\n180 cm\n```\n\n```output\nHeight in centimeters?\nPlease type just the number, like 172\nThanks!\n```\n\n- The `try` block holds the code that might throw. Java runs it line by line, as usual.\n- When a line in it throws, the rest of the `try` block is skipped (here the line that prints the meters), and Java looks at the `catch` block. `catch (NumberFormatException e)` catches exceptions of the class NumberFormatException, so its block runs.\n- `e` is a variable, a bit like a parameter, that holds the exception object. This `catch` block doesn't use it; the last step of this module shows what it can tell you.\n- After the `catch` block, the program goes on as usual: `Thanks!` is printed.\n- When nothing in the `try` block throws, the `catch` block is skipped. With the input `180`, the program prints `That's 1.8 meters` and `Thanks!`.\n\nModule 20 wrote `catch (Exception e)`, which catches every kind of exception. `catch (NumberFormatException e)` catches only this kind, and that's often better: its message, \"type just the number\", fits this problem and no other. Any other exception still shows up as a crash with its report, instead of hiding behind a message that doesn't fit. The next step is about choosing what to catch.\n\n`Integer.valueOf` throws a NumberFormatException for the same texts, and `Double.parseDouble` throws one for a text that isn't a decimal number, such as `1,80` with a comma.\n\n### Asking again\n\nIn a loop, a caught exception can simply mean \"try again\". This method asks until the user types a whole number:\n\n```java\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        int height = readHeight(scanner);\n        System.out.println(\"That's \" + (height / 100.0) + \" meters\");\n    }\n\n    public static int readHeight(Scanner scanner) {\n        while (true) {\n            System.out.println(\"Height in centimeters?\");\n            try {\n                return Integer.parseInt(scanner.nextLine());\n            } catch (NumberFormatException e) {\n                System.out.println(\"Not a whole number, try again\");\n            }\n        }\n    }\n}\n```\n\n```input\ntall\n1.80\n180\n```\n\n```output\nHeight in centimeters?\nNot a whole number, try again\nHeight in centimeters?\nNot a whole number, try again\nHeight in centimeters?\nThat's 1.8 meters\n```\n\nThe `return` in the `try` block ends the method only when `parseInt` succeeds. When `parseInt` throws, the `return` never happens: the `catch` block prints its message, and the loop goes around again.\n","task":"Fill in the blanks. The program reads a temperature in degrees Celsius, a whole number, and prints it in Fahrenheit. If the line isn't a whole number, it prints `Not a temperature: ` and the line instead. With the input `21.5` it prints:\n\n```\nNot a temperature: 21.5\n```\n","kind":"fill","seed":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        String line = scanner.nextLine();\n        [[try]] {\n            int celsius = Integer.parseInt(line);\n            System.out.println(celsius + \" C is \" + (celsius * 9 / 5 + 32) + \" F\");\n        } catch ([[NumberFormatException‖Exception‖IllegalArgumentException‖RuntimeException]] e) {\n            System.out.println(\"Not a temperature: \" + line);\n        }\n    }\n}\n","solution":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        String line = scanner.nextLine();\n        try {\n            int celsius = Integer.parseInt(line);\n            System.out.println(celsius + \" C is \" + (celsius * 9 / 5 + 32) + \" F\");\n        } catch (NumberFormatException e) {\n            System.out.println(\"Not a temperature: \" + line);\n        }\n    }\n}\n","hints":["The block of code that might throw starts with the word `try`.","`Integer.parseInt` throws a `NumberFormatException` when the text isn't a whole number: that's the class to catch."],"tests":[{"name":"Test 1","stdin":"21.5\n","expect":"Not a temperature: 21.5","hidden":false},{"name":"Test 2","stdin":"20\n","expect":"20 C is 68 F","hidden":false},{"name":"Test 3","stdin":"-40\n","expect":"-40 C is -40 F","hidden":true},{"name":"Test 4","stdin":"warm\n","expect":"Not a temperature: warm","hidden":true},{"name":"Test 5","stdin":"37\n","expect":"37 C is 98 F","hidden":true}],"require":[],"forbid":[],"more":[{"task":"A kitchen scale app reads weights in grams, one per line, until the line `done`. It adds up the lines that are whole numbers. For every other line, it prints `Not grams: ` and the line, and carries on with the next one. At the end it prints the total. For the input shown, it prints:\n\n```\nNot grams: 1 kg\nNot grams: half\nTotal: 1250 g\n```\n","kind":"code","seed":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n\n    }\n}\n","solution":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        int total = 0;\n        while (true) {\n            String line = scanner.nextLine();\n            if (line.equals(\"done\")) {\n                break;\n            }\n            try {\n                total += Integer.parseInt(line);\n            } catch (NumberFormatException e) {\n                System.out.println(\"Not grams: \" + line);\n            }\n        }\n        System.out.println(\"Total: \" + total + \" g\");\n    }\n}\n","hints":["Read in a `while (true)` loop that ends at `done`, with the total in a variable made before the loop.","Inside the loop, put `total += Integer.parseInt(line);` in a `try` block, and print the message in the `catch (NumberFormatException e)` block."],"tests":[{"name":"Test 1","stdin":"250\n1 kg\n500\nhalf\n500\ndone\n","expect":"Not grams: 1 kg\nNot grams: half\nTotal: 1250 g","hidden":false},{"name":"Test 2","stdin":"done\n","expect":"Total: 0 g","hidden":true},{"name":"Test 3","stdin":"70\n30\n-20\n12.5\nten\ndone\n","expect":"Not grams: 12.5\nNot grams: ten\nTotal: 80 g","hidden":true}],"require":[{"pattern":"\\bcatch\\s*\\(","message":"Let `Integer.parseInt` try every line, and catch its exception with `try` and `catch`."}],"forbid":[]},{"task":"What does this program print? Follow each round of the loop into the `try` block, and out of it.\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        String[] readings = {\"12\", \"7x\", \"30\", \"\", \"-4\"};\n        int total = 0;\n        int bad = 0;\n        for (String reading : readings) {\n            try {\n                total += Integer.parseInt(reading);\n                System.out.println(\"added \" + reading);\n            } catch (NumberFormatException e) {\n                bad++;\n            }\n        }\n        System.out.println(total + \" \" + bad);\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        String[] readings = {\"12\", \"7x\", \"30\", \"\", \"-4\"};\n        int total = 0;\n        int bad = 0;\n        for (String reading : readings) {\n            try {\n                total += Integer.parseInt(reading);\n                System.out.println(\"added \" + reading);\n            } catch (NumberFormatException e) {\n                bad++;\n            }\n        }\n        System.out.println(total + \" \" + bad);\n    }\n}\n","hints":["`Integer.parseInt(\"7x\")` throws, so `total` doesn't change and the `println` after it is skipped. The empty text `\"\"` isn't a number either.","A minus sign is fine: `-4` is a whole number. Each caught exception adds one to `bad`, and the loop goes on with the next reading."],"tests":[{"name":"Output","stdin":"","expect":"added 12\nadded 30\nadded -4\n38 2","hidden":false}],"require":[],"forbid":[],"lines":["added 12","added 30","added -4","38 2"]}]}