What an Exception Is
An exception is Java's way of saying "something went wrong and I can't continue normally." Instead of returning a bad value or silently corrupting your data, the runtime creates an exception object describing the problem and throws it. Normal execution stops at that point, and Java starts hunting for code that knows how to deal with the situation.
If nobody handles it, the exception reaches the top of your program, the JVM prints a stack trace, and the process exits with a non-zero status.
Notice that "after" never prints. The instant numbers[5] is evaluated, an ArrayIndexOutOfBoundsException is thrown and the rest of main is abandoned - reaching past the last valid index of an array is one of the most common ways to trigger an exception.
Reading a Stack Trace
When an exception goes unhandled, you get output like this. It looks intimidating, but it is just a list:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Main.divide(Main.java:8)
at Main.main(Main.java:4)
Read it top to bottom:
- The first line is the type (
ArithmeticException) and a message (/ by zero). - Each
at ...line is a stack frame. The top one is where the exception was actually thrown -divideon line 8. Below it is the caller,mainon line 4.
The top at line is where you look first. The frames beneath it answer "how did we get here?"
Run this and the trace points straight at the a / b line, then shows main as the caller. That call chain is the single most useful debugging tool Java gives you for free.
The Exception Hierarchy
Every exception is an object, and they all descend from Throwable. The two branches that matter:
Error- serious problems the JVM raises, likeOutOfMemoryErrororStackOverflowError. You generally do not catch these.Exception- problems your program can reasonably anticipate and handle. Inside it sitsRuntimeException, the parent of the everyday bugs likeNullPointerException.
Throwable
├── Error (don't catch: OutOfMemoryError, StackOverflowError)
└── Exception
├── IOException (checked)
├── SQLException (checked)
└── RuntimeException (unchecked)
├── NullPointerException
├── ArithmeticException
└── ArrayIndexOutOfBoundsException
Because these are real classes, an exception can carry a message and you can ask it about itself:
getMessage() returns the text after the colon in the stack trace; getClass().getSimpleName() gives you the exception type by name.
Checked vs Unchecked
This is the distinction that trips up newcomers most.
- Unchecked exceptions extend
RuntimeException. They usually mean a bug in your code - anullyou didn't expect, a bad index, dividing by zero. The compiler does not force you to handle them. - Checked exceptions extend
Exceptionbut notRuntimeException(for exampleIOException). They represent conditions outside your control - a missing file, a dropped network connection. The compiler forces you to either catch them or declare them.
If a method can throw a checked exception, it must say so with throws, and every caller must deal with it:
Remove throws Exception from main and the code won't even compile - that is the compiler enforcing the checked contract. Unchecked exceptions never require this.
Throwing Your Own
You don't only react to exceptions - you can raise them. Use throw with a new exception object to signal that an argument or state is invalid. This is far better than returning a magic value like -1 and hoping the caller checks for it.
Always include a message that explains what was wrong and ideally the offending value - your future self reading the stack trace will thank you. IllegalArgumentException and IllegalStateException are the two you'll reach for most when validating inputs.
Common Gotchas
- Swallowing exceptions. Catching an exception and doing nothing (an empty block) hides bugs. At minimum log it; usually you should handle or rethrow it.
- Catching
Exceptiontoo broadly. Catching the baseException(or worse,Throwable) can mask problems you didn't intend to handle. Catch the specific type you expect. - Reading the trace bottom-up. The most relevant frame is the top
atline, not the bottom. Start there. - Confusing
ErrorwithException. Don't try to recover fromOutOfMemoryErrororStackOverflowError; fix the underlying cause instead.
Next: try-catch
Now you know what exceptions are and how to read them. The next page covers how to actually handle them: wrapping risky code in a try block, recovering in catch, and running cleanup code in finally so your program keeps going instead of crashing.
Frequently Asked Questions
What is an exception in Java?
An exception is an object that represents a problem detected while a program runs - like dividing by zero, indexing past the end of an array, or calling a method on null. When the problem occurs, Java throws the exception: it stops the normal flow and looks for code that can handle it. If nothing handles it, the program prints a stack trace and exits.
What is the difference between a checked and an unchecked exception in Java?
Checked exceptions (subclasses of Exception but not RuntimeException, e.g. IOException) must be either caught or declared with throws - the compiler enforces it. Unchecked exceptions (subclasses of RuntimeException, e.g. NullPointerException, ArrayIndexOutOfBoundsException) usually signal programming bugs and need no declaration. Error (like OutOfMemoryError) is also unchecked and generally not meant to be caught.
How do I read a Java stack trace?
Read it top-down. The first line names the exception type and message (e.g. java.lang.ArithmeticException: / by zero). Each at ... line below is a stack frame showing the method, file, and line number, starting with where the exception was thrown and walking back through the callers. The top at line is almost always where to look first.