Exceptions
In short: Runtime errors that can be specifically caught and handled with try/catch, instead of crashing the entire program.
In more detail: Java distinguishes checked exceptions (e.g. IOException — the compiler forces you to catch or rethrow) and unchecked ones (e.g. NullPointerException — the compiler checks nothing, usually a programming error rather than an expectable state). A finally block always runs, regardless of whether an exception was thrown or not — typical for cleanup work.
try {
int result = 10 / number;
} catch (ArithmeticException e) {
System.out.println("Division by zero!");
}In Depth
class NotEnoughFundsException extends Exception { // checked - custom exception class
public NotEnoughFundsException(String message) {
super(message);
}
}
class Account {
private double balance = 100.0;
// "throws" forces the caller to handle or rethrow the exception
void withdraw(double amount) throws NotEnoughFundsException {
if (amount > balance) {
throw new NotEnoughFundsException("Only " + balance + "€ available");
}
balance -= amount;
}
}
Account acc = new Account();
try {
acc.withdraw(150.0);
} catch (NotEnoughFundsException e) {
System.out.println("Error: " + e.getMessage());
} finally {
System.out.println("Always runs, exception or not");
}Checked exceptions (subclasses of Exception, but not of RuntimeException) force the compiler check: every method that can throw a checked exception must declare it via throws, and every caller must either catch it or rethrow it themselves — otherwise the code doesn’t compile. Unchecked exceptions (subclasses of RuntimeException, e.g. NullPointerException) don’t require this, because they usually represent programming errors rather than expectable states that could theoretically occur anywhere. Custom domain-specific errors (like NotEnoughFundsException above) are usually modelled as a checked exception when the caller can and should meaningfully handle the error — for pure programming errors (incorrect use of an API), RuntimeException is more common.
The exception chain (caused by)
When catching an exception, you can throw a NEW, more informative exception without losing the original cause — the second constructor parameter (cause) chains both, so the stack trace eventually shows both levels (“Caused by: …”):
try {
loadConfiguration();
} catch (IOException e) {
throw new RuntimeException("Configuration could not be loaded", e); // e chained as the cause
}Without this chaining, the original technical cause (e.g. “file not found”) would be lost — the log would only show the new, more general error message, which makes troubleshooting significantly harder.
Best practice: don’t swallow
A common beginner mistake is an empty catch block that completely “swallows” an exception:
try {
riskyOperation();
} catch (Exception e) {
// NOTHING - the error disappears without a trace, the program appears to run normally
}This leads to particularly hard-to-find bugs, since the program doesn’t crash, but also doesn’t deliver the expected result. At least one log entry (e.printStackTrace() or better, an actual logging framework) should be in every catch block, even if the error is meant to be deliberately ignored.
See also: Multiple Exceptions, try-with-resources, Errors