try, catch and finally
An exception doesn't have to crash the program. You can catch it and decide what to do next: show a clear message, use a default value, try again. The try-catch statement is made for this.
try and catch
Code that may throw an exception goes into a try block. It is followed by a catch block with the type of exception to catch:
void main() {
try {
int result = 10 / 0;
IO.println(result);
} catch (ArithmeticException e) {
IO.println("You can't divide by zero");
}
IO.println("The program keeps running");
}Output:
You can't divide by zero
The program keeps runningWhen an exception occurs in try, the rest of the block is skipped and control jumps to the matching catch. After it the program continues as usual. If there was no exception, catch doesn't run at all.
The exception object
The parentheses of catch declare a variable, usually called e. It gives access to the error message:
void main() {
String input = "12a";
try {
int number = Integer.parseInt(input);
IO.println(number * 2);
} catch (NumberFormatException e) {
IO.println("Error: " + e.getMessage());
}
}Output:
Error: For input string: "12a"Several catch blocks
One try can have several catch blocks — a different reaction for each error. Java checks them from top to bottom and runs the first one that matches. If the reaction is the same, you can list the types with | in a single block: catch (NumberFormatException | ArithmeticException e) — this is called multi-catch.
void check(String text, int index) {
try {
int value = Integer.parseInt(text);
int[] data = new int[3];
data[index] = value;
IO.println("Stored: " + value);
} catch (NumberFormatException e) {
IO.println("Not a number: " + text);
} catch (ArrayIndexOutOfBoundsException e) {
IO.println("Bad index: " + index);
}
}
void main() {
check("7", 1);
check("seven", 1);
check("7", 5);
}Output:
Stored: 7
Not a number: seven
Bad index: 5finally
The finally block runs always: whether there was an exception or not, whether it was caught or not. It is usually where resources are released — files and connections are closed:
void main() {
try {
IO.println("Opening the connection");
int x = 5 / 0;
} catch (ArithmeticException e) {
IO.println("Error: " + e.getMessage());
} finally {
IO.println("Closing the connection");
}
}Output:
Opening the connection
Error: / by zero
Closing the connectionThere is a handier way to close resources — try-with-resources, which has a lesson of its own.
throw
You can throw exceptions yourself with the throw statement. This is how a method reports that it got invalid data:
void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative: " + age);
}
IO.println("Age: " + age);
}
void main() {
setAge(30);
try {
setAge(-5);
} catch (IllegalArgumentException e) {
IO.println(e.getMessage());
}
}Output:
Age: 30
Age cannot be negative: -5Common mistake
catch blocks are checked in order, so a general type can't come before a specific one. Exception also catches ArithmeticException, so the second block could never run — the compiler treats this as an error:
void main() {
try {
int x = 10 / 0;
} catch (Exception e) {
IO.println("Something went wrong");
} catch (ArithmeticException e) {
IO.println("Division by zero");
}
}Output:
Main.java:6: error: exception ArithmeticException has already been caught
} catch (ArithmeticException e) {
^
1 error
error: compilation failedPut specific exceptions above general ones. And don't overuse catch (Exception e): it's better to catch only the errors you really know how to handle.