throws clause on signaturepublic void readFile(String path) throws IOException {
FileReader file = new FileReader(path); // throws checked IOException
}The compiler does not require you to handle these. They typically represent bugs in your code — things that should not happen if the program is written correctly.
Exceptions in Java are classes organized using inheritance, just like any other class hierarchy.
Throwable
├── Error (serious JVM problems -- don't catch these)
│ ├── OutOfMemoryError
│ └── StackOverflowError
└── Exception (things programs should handle)
├── IOException (checked)
├── SQLException (checked)
└── RuntimeException (unchecked)
├── NullPointerException
├── ArrayIndexOutOfBoundsException
└── IllegalArgumentException
Because exceptions use inheritance, a catch block that catches a parent class will also catch all its subclasses:
An exception is just a regular Java object — it must be instantiated before it can be thrown. These are two separate steps:
// Instantiating only -- creates the object but nothing happens
new IllegalArgumentException("bad value");
// Throwing only -- won't compile, throw needs an exception object
throw;
// Instantiating AND throwing -- what you actually do
throw new IllegalArgumentException("bad value");You can also separate the steps:
// Instantiate first
IllegalArgumentException ex = new IllegalArgumentException("bad value");
// Throw later
throw ex;The throw keyword is what triggers the exception and unwinds the call stack — simply creating the object does nothing on its own.
You can build your own Exception class – meaningful name, carries useful data, can be caught specifically. Example:
// Thrown when a withdrawal exceeds the available balance
public class InsufficientFundsException extends Exception {
private double amount;
private double balance;
public InsufficientFundsException(double amount, double balance) {
super("Cannot withdraw $" + amount + ". Current balance: $" + balance);
this.amount = amount;
this.balance = balance;
}
}finally BlockThe finally block executes no matter what — whether an exception was thrown or not, whether it was caught or not.
try {
System.out.println("try");
int result = 10 / 0; // throws ArithmeticException
} catch (ArithmeticException e) {
System.out.println("catch");
} finally {
System.out.println("finally"); // ALWAYS runs
} try
catch
finally
finally BlockEven if the exception is not caught, finally still runs:
try {
System.out.println("try");
int result = 10 / 0;
} finally {
System.out.println("finally"); // still runs before program crashes
} try
finally
Exception in thread "main"...
What is the advantage to creating an entirely new Exception class (and throwing it) over just throwing an instance of an existing Exception with a custom “message?”