Chapter 10
Exceptions and enums
So far, a program simply stopped when something went wrong, for example with Integer.parseInt("abc"). With exceptions you catch such errors and react to them, or report an error yourself. On top of that come enums: types with a fixed list of values, like the phases of a traffic light.
Catching exceptions
Code that can go wrong goes in a try block. If an exception is thrown there, Java jumps straight to the matching catch block. The rest of the try block is skipped. After that, the program continues normally.
try {
int value = Integer.parseInt(text);
} catch (NumberFormatException e) {
// text was not a number
}
e.getMessage() returns the error message. A finally block always runs, whether it worked or not, even if there's a return in the try. Put there what must happen in any case, such as closing a connection.
public class Main {
public static void main(String[] args) {
String[] inputs = {"42", "abc", "7"};
int sum = 0;
for (String input : inputs) {
try {
int value = Integer.parseInt(input);
sum += value;
System.out.println("Added " + value);
} catch (NumberFormatException e) {
System.out.println("Not a number: " + input);
} finally {
System.out.println("Checked " + input);
}
}
System.out.println("Sum: " + sum);
}
}Throwing exceptions and your own exceptions
With throw new IllegalArgumentException("No values"); you report an error yourself. The method ends at that point, and the caller gets the exception.
Your own exception is a class that extends Exception. Its constructor passes the message on with super(...). There are two kinds:
- Checked: everything that extends
Exception. The compiler requires you to catch it or announce it withthrowsin the method header:void buyApple() throws OutOfStockException. - Unchecked: everything that extends
RuntimeException, e.g.IllegalArgumentExceptionandNumberFormatException. Here the compiler requires nothing.
A throws passes the exception on to the caller, who then has to deal with it.
class OutOfStockException extends Exception {
public OutOfStockException(String product) {
super(product + " is out of stock");
}
}
class Shop {
private int apples = 2;
public void buyApple() throws OutOfStockException {
if (apples == 0) {
throw new OutOfStockException("Apple");
}
apples--;
}
}
public class Main {
public static double divide(double a, double b) {
if (b == 0) {
throw new IllegalArgumentException("Cannot divide by 0");
}
return a / b;
}
public static void main(String[] args) {
try {
System.out.println(divide(10, 4));
System.out.println(divide(1, 0));
System.out.println("This line never runs");
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
}
Shop shop = new Shop();
for (int i = 1; i <= 3; i++) {
try {
shop.buyApple();
System.out.println("Bought apple " + i);
} catch (OutOfStockException e) {
System.out.println(e.getMessage());
}
}
}
}Enums
enum Season { SPRING, SUMMER, AUTUMN, WINTER } creates a type with exactly four values. A variable of type Season can't hold anything else; the compiler catches typos.
- You compare enum values with
==. values()returns all values as an array, in the order they are declared.ordinal()is the position, from 0.- As text, a value gives its name, e.g.
SUMMER. - In a
switch, you write the values without the class name:case SUMMER ->.
Like a class, an enum can have fields, a constructor and methods. The values in parentheses after each constant go to the constructor: EURO(100).
enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
enum Coin {
CENT(1), TEN_CENTS(10), EURO(100);
private final int cents;
Coin(int cents) {
this.cents = cents;
}
public int getCents() {
return cents;
}
}
public class Main {
public static void main(String[] args) {
Season now = Season.SUMMER;
System.out.println(now);
System.out.println("Position: " + now.ordinal());
System.out.println(now == Season.SUMMER);
for (Season s : Season.values()) {
System.out.print(s + " ");
}
System.out.println();
String advice = switch (now) {
case SUMMER -> "Sunscreen";
case WINTER -> "Gloves";
default -> "Umbrella";
};
System.out.println(advice);
int total = 0;
for (Coin c : Coin.values()) {
total += c.getCents();
}
System.out.println("All coins: " + total + " cents");
}
}Common mistakes
Checked exception not handled
If you call a method with
throws OutOfStockExceptionwithouttry/catchand without your ownthrows, the compiler reportsunreported exception OutOfStockException; must be caught or declared to be thrown.Wrong order of catch blocks
Java takes the first matching
catch. Ifcatch (Exception e)comes beforecatch (NumberFormatException e), the second can never apply. The compiler reportsexception NumberFormatException has already been caught. The more specific exception goes first.Empty catch block
catch (Exception e) { }swallows every error, including ones you didn't expect. Only catch the exception you expect, and react to it visibly.Dividing by 0
With
int,10 / 0throws anArithmeticException. Withdoublethere's no exception:10.0 / 0givesInfinity. That's whydividein the example checks itself and throws an exception.