Exception Handling in Java
Stop programs from crashing on bad input: try, catch and finally, checked vs unchecked exceptions, throw, throws and your own exception classes.
Learning objectives
- βExplain what an exception is and what happens when nobody handles it
- βHandle exceptions with try, catch and finally
- βTell checked exceptions apart from unchecked ones
- βUse throw and throws, and write a custom exception class
π‘ Key points
- An exception is a problem that happens while the program runs, such as dividing by zero. If nobody catches it, the program stops and prints a stack trace.
- Put risky code inside try. A catch (Type e) block handles a matching exception, and the program then carries on after the try-catch.
- finally runs whether or not an exception happened. Use it for clean-up such as closing a file.
- Catch blocks are checked from top to bottom, so put specific exceptions (ArithmeticException) before general ones (Exception).
- Checked exceptions (IOException, FileNotFoundException) must be caught or declared with throws, and the compiler enforces this. Unchecked exceptions (subclasses of RuntimeException) do not have to be.
- throw raises an exception right now. throws, in a method header, warns callers that the method may pass one on to them.
- A custom exception is a class that extends Exception (checked) or RuntimeException (unchecked).
π» Code examples(6)
public class Crash {
public static void main(String[] args) {
int total = 500;
int students = 0;
System.out.println("Calculating average...");
int avg = total / students;
System.out.println("Average: " + avg);
}
}
Calculating average... Exception in thread "main" java.lang.ArithmeticException: / by zero at Crash.main(Crash.java:6)
public class SafeDivide {
public static void main(String[] args) {
int total = 500;
int students = 0;
try {
int avg = total / students;
System.out.println("Average: " + avg);
} catch (ArithmeticException e) {
System.out.println("Error: " + e.getMessage());
}
System.out.println("Program continues");
}
}
Error: / by zero Program continues
public class ParseMarks {
public static void main(String[] args) {
String[] inputs = {"85", "abc", "92"};
int[] marks = new int[2];
for (int i = 0; i < inputs.length; i++) {
try {
marks[i] = Integer.parseInt(inputs[i]);
System.out.println("Saved " + marks[i]);
} catch (NumberFormatException e) {
System.out.println("Not a number: "
+ inputs[i]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("No space for "
+ inputs[i]);
} finally {
System.out.println("-- done with " + i);
}
}
}
}
Saved 85 -- done with 0 Not a number: abc -- done with 1 No space for 92 -- done with 2
import java.io.FileReader;
import java.io.IOException;
public class ReadFile {
public static void main(String[] args) {
try {
FileReader fr = new FileReader("marks.txt");
System.out.println("File opened");
fr.close();
} catch (IOException e) {
System.out.println("Could not open marks.txt");
}
}
}
Could not open marks.txt
public class Ticket {
static void book(int age) {
if (age < 18) {
throw new IllegalArgumentException(
"Age " + age + " is below 18");
}
System.out.println("Booked for age " + age);
}
public static void main(String[] args) {
try {
book(21);
book(15);
book(30);
} catch (IllegalArgumentException e) {
System.out.println("Refused: "
+ e.getMessage());
}
}
}
Booked for age 21 Refused: Age 15 is below 18
class LowBalanceException extends Exception {
LowBalanceException(String msg) {
super(msg);
}
}
class Wallet {
private double balance = 1000;
void pay(double amt) throws LowBalanceException {
if (amt > balance) {
throw new LowBalanceException("Need Rs."
+ amt + ", have Rs." + balance);
}
balance -= amt;
System.out.println("Paid Rs." + amt
+ ", left Rs." + balance);
}
}
public class WalletDemo {
public static void main(String[] args) {
Wallet w = new Wallet();
try {
w.pay(400);
w.pay(800);
} catch (LowBalanceException e) {
System.out.println("Failed: "
+ e.getMessage());
}
}
}
Paid Rs.400.0, left Rs.600.0 Failed: Need Rs.800.0, have Rs.600.0
π― Practice
Q1. What does finally do, and when does it run?+
It holds code that runs after the try (and any catch), whether or not an exception happened, for example closing a file. It is skipped only if the JVM itself stops, such as with System.exit().
Q2. Is NumberFormatException checked or unchecked? How can you tell?+
Unchecked. It extends IllegalArgumentException, which extends RuntimeException, so the compiler does not force you to catch it.
Q3. What is the difference between throw and throws?+
throw is a statement that raises one exception object: throw new ArithmeticException("..."); throws goes in a method header and lists the exceptions the method may pass to its caller: void read() throws IOException
Q4. Why is this a compile error? catch (Exception e) { } catch (ArithmeticException e) { }+
Exception already catches every ArithmeticException, so the second block could never run. javac reports 'exception ArithmeticException has already been caught'. Put the specific catch first.
Q5. Write a custom unchecked exception called InvalidMarksException.+
class InvalidMarksException extends RuntimeException { InvalidMarksException(String msg) { super(msg); } } // use: throw new InvalidMarksException("Marks must be 0-100");
π Notes
The exception family tree
Throwableis the root of everything that can be thrown.Errorcovers serious problems inside the JVM, such asStackOverflowErrorandOutOfMemoryError. Don't catch these.Exceptioncovers problems a program can recover from. It and its subclasses, such asIOException, are checked, except for one branch:RuntimeExceptionand its subclasses are unchecked:ArithmeticException,NullPointerException,ArrayIndexOutOfBoundsException,NumberFormatException.
Unchecked exceptions usually mean a bug you can fix with a check, such as if (students != 0). Checked exceptions are problems outside your control, such as a missing file, so Java makes you plan for them. Forget one and you see unreported exception FileNotFoundException; must be caught or declared to be thrown.
try-with-resources (Java 7+)
Anything that must be closed, such as a file or a Scanner, can be opened in the brackets after try. Java closes it for you, even if an exception happens:
try (Scanner sc = new Scanner(System.in)) {
int n = sc.nextInt();
System.out.println("Read " + n);
} // sc.close() happens automatically
Common mistakes
- An empty catch block.
catch (Exception e) { }hides the problem completely. At least printe.getMessage(). - Catching
Exceptioneverywhere. Catch the specific type you expect, so real bugs still show up. - Using exceptions instead of a simple check. Test
if (index < arr.length)rather than waiting forArrayIndexOutOfBoundsException. - Putting the general catch first. It makes the later, specific catches unreachable, which is a compile error.
Next: Day 19, Recursion. You will write methods that call themselves, and learn why every one needs a base case.