throw and your own exceptions
In the lesson on checked exceptions, the method load threw an exception itself for the first time, with the throw statement. In the building, this is the fire alarm button on the wall: anyone who notices smoke can press it without waiting for a sensor. Let's see when to press it yourself and how to make an alarm with your own label.
Say the method withdraw takes money from an account. What should it do when asked for more than there is? It could return -1, but the caller may forget to check that and print -1 as the balance. An exception cannot be forgotten: if no one answers it, the program stops.
Your own exception class
Java has no ready class for "not enough money", but you can write one. An exception is an ordinary class, a subclass of Exception:
public class Main {
public static void main(String[] args) {
var error = new InsufficientFundsException("need 50 more");
System.out.println(error.getMessage()); // need 50 more
}
}
class InsufficientFundsException extends Exception {
InsufficientFundsException(String message) {
super(message);
}
}Let's take it apart:
class InsufficientFundsException extends Exceptionis a new exception class. Its parent isException, so the exception is checked. By tradition the name ends inException.- The constructor takes a message and passes it to the parent through
super(message). - The method
getMessage()is declared in the classThrowable, and our class inherits it. It returns the message that was passed to the constructor. - In
mainthe object is created withnew, like any other. Nothing happens: until an exception is thrown, it is an ordinary object.
If the class inherits from RuntimeException, the exception is unchecked. A checked one is chosen for a situation the caller must be ready for, like a lack of money. An unchecked one is for an error in the caller's code.
Throwing your own exception
Now withdraw can press the alarm button itself:
static int withdraw(int balance, int amount)
throws InsufficientFundsException {
if (amount <= 0) {
throw new IllegalArgumentException("amount must be positive");
}
if (amount > balance) {
int lack = amount - balance;
throw new InsufficientFundsException("need " + lack + " more");
}
return balance - amount;
}The method checks for two different troubles:
- An amount of zero or less is an error in the code of whoever called the method. Java has a ready unchecked class for it,
IllegalArgumentException, "a wrong argument". TheNumberFormatExceptionthatparseIntthrows is its subclass. - Not enough money is an ordinary real-life situation, and the caller must be ready for it. Our checked exception reports it, so the header says
throws InsufficientFundsException.
throw ends the method at once, like an exception from the JVM: return is never reached. A call to withdraw is now written inside try:
try {
System.out.println(withdraw(100, 30)); // 70
System.out.println(withdraw(100, 150));
} catch (InsufficientFundsException e) {
System.out.println("Refused: " + e.getMessage()); // Refused: need 50 more
}Let's go line by line:
withdraw(100, 30)returns70, and the program prints it.withdraw(100, 150)throws an exception with the messageneed 50 more, and the secondprintlndoes not run.catchcatches it and printsRefused: need 50 more.
And a call withdraw(100, -5) in the same try would throw an IllegalArgumentException. This catch does not catch it, because the class is different, and the program stops:
Exception in thread "main" java.lang.IllegalArgumentException: amount must be positiveThat is the intent: an error in the code is not hidden but fixed.
throw and throws
These two words get mixed up all the time, and interviewers often ask how they differ:
| Word | Where it goes | What it does |
|---|---|---|
throw | in the method body | throws one exception object now |
throws | in the method header, after the parentheses | declares which exceptions may leave the method |
throw is an action: pressing the alarm button. throws is the "we pass it upstairs" sign on the floor. For a checked exception the sign is required, for an unchecked one it is not.
Here the button picture is imprecise. A real siren sounds the same whoever switched it on. An exception carries its own class and message, and by the class catch decides whether this alarm is its own.
Common mistake
In your own exception class you forgot to pass the message to the parent:
class InsufficientFundsException extends Exception {
InsufficientFundsException(String message) { // ✗
}
}The program compiles, but instead of Refused: need 50 more it prints Refused: null. The constructor did not call super(message), so the compiler inserted super() with no arguments. The parent never learned the message, and getMessage() returns null. Pass the message to the parent: write super(message); as the constructor's first line.
Key points
throw new …throws an exception at once and ends the method; a wrong argument has the readyIllegalArgumentException.- Your own exception is a subclass of
Exception(checked) orRuntimeException(unchecked) that passes its message throughsuper(message). getMessage()returns the message passed to the constructor.throwthrows an exception in the method body, andthrowsdeclares it in the header.