Exception Handling
In the real world things go wrong — files vanish, input is garbage, networks drop. Learn to catch those failures and keep your program running instead of crashing.
Learn Exception Handling in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
📚 Before You Start
This lesson builds on a few earlier ones. You should be comfortable with:
- Methods — calling them and returning values
- Classes & objects — constructors and extends (inheritance)
- Control flow — if / else
Real-World Analogy
An exception is like a fire alarm in a building. When something goes badly wrong, you don't want everyone to freeze and the building to collapse — you want an emergency procedure that takes over.
throw = pulling the alarm: "something is wrong, stop normal work now."
try = the part of the building wired to the alarm system.
catch = the trained response: evacuate, call for help, recover.
finally = locking up afterwards — it happens whether or not there was a fire.
Without this, one bad input would crash the whole program. With it, you detect the problem, respond, and carry on.
1️⃣ try / catch / finally — the basic shape
You put risky code — anything that might fail — inside a try block. If it throws an exception, Java jumps straight to a matching catch block and runs your recovery code. The finally block runs afterwards no matter what , so it's where cleanup goes.
Every exception object carries a message you can read with e.getMessage() and a type you can read with e.getClass().getSimpleName() . The next example uses both.
2️⃣ The exception hierarchy
Every error in Java is an object, and they all descend from one root class, Throwable . The shape of this tree decides what you must catch versus what's optional:
Error means the JVM itself is broken (out of memory, infinite recursion). You normally let these crash — there's nothing useful you can do.
Checked exceptions extend Exception but not RuntimeException . The compiler refuses to build until you handle them. They model recoverable situations a caller should plan for.
Unchecked exceptions extend RuntimeException . They usually mean a bug in your code — a null you forgot to check, an index off the end of an array.
3️⃣ throw vs throws (two different words!)
throw (no s ) is a statement — it actually raises an exception right now. throws (with s ) is part of a method signature — it warns callers that this method might raise a checked exception.
4️⃣ Multi-catch — one block, several types
When two different exceptions need the same handling, don't write two near-identical catch blocks. Join the types with a single pipe | :
Order still matters when types are related: a more specific type must come before its parent, or you'll get a compile error for unreachable code.
5️⃣ Custom exceptions — name your failures
A custom exception is just a class that extends an existing exception. Extend Exception for a checked one (callers must handle it) or RuntimeException for an unchecked one. The payoff: a name that says exactly what went wrong, plus room for extra data.
Now a caller can write catch (InsufficientFundsException e) and read e.getDeficit() — far clearer than catching a generic RuntimeException and guessing.
6️⃣ Try-with-resources (Java 7+)
Analogy: it's a self-closing door — you walk through and it shuts behind you. Declare a resource in the try (...) parentheses and Java calls its close() method automatically when the block ends, even if an exception is thrown. The resource just has to implement AutoCloseable .
You can also chain exceptions: when you catch a low-level error and rethrow a higher-level one, pass the original as the second argument so the cause isn't lost. Read it back with e.getCause() . The next example shows both.
🎯 Your Turn #1 — try / catch / finally
Fill in the three blanks so the program catches a bad array index and still runs its cleanup line. The expected output is in the comments — match it exactly.
🎯 Your Turn #2 — throw a custom checked exception
Complete the custom exception so it's checked , then raise it with the right keyword. The throws clause and the catch are already wired up for you.
🧩 Mini-Challenge — Safe config loader
Now with the scaffolding removed. Read the brief in the comments and write the method yourself — only the outline is given. Use a try / catch (NumberFormatException e) to fall back to a default when the text isn't a number.
Common Errors (and how to fix them)
Fix: at minimum log it, or rethrow it. A silent failure is the hardest bug to find: catch (IOException e) { log.error("read failed", e); }
Fix: catch the most specific type you can actually recover from, and let real bugs propagate: catch (FileNotFoundException e) before catch (IOException e) .
❌ Leaking resources without try-with-resources
Fix: declare the resource in try (...) so close() always runs: try (var conn = openConnection()) { conn.use(); }
Fix: that message means a checked exception escaped unhandled. Either wrap the call in try/catch or add throws IOException to the method. Unchecked ( RuntimeException ) types never produce this error.
Don't replace an if with a try/catch. Exceptions are far slower than a plain conditional and they hide your logic — reserve them for genuinely exceptional situations.
📋 Quick Reference
Keyword / idea
Syntax
Purpose
try
try { risky }
Wrap code that might fail
catch
catch (IOException e)
Handle one specific error
finally
finally { cleanup }
Always runs — cleanup
throw
throw new Ex("msg")
Raise an exception now
throws
void f() throws IOException
Declare a checked exception
multi-catch
catch (A | B e)
One block, several types
try-with-resources
try (var r = open())
Auto-close AutoCloseable
checked
extends Exception
Compiler forces handling
unchecked
extends RuntimeException
Optional to catch (bugs)
❓ Frequently Asked Questions
🎉 Lesson Complete!
You can now write robust Java that survives the unexpected — wrap risky code in try / catch / finally , read the Throwable hierarchy, tell checked from unchecked, raise errors with throw and declare them with throws , collapse handlers with multi-catch, auto-close resources with try-with-resources, and design custom exceptions that name your failures.
Next up: Collections Framework — work with ArrayList , HashMap , HashSet , and the data structures real programs are built on.
Practice quiz
Which block runs whether or not an exception was thrown?
- try
- catch
- finally
- throw
Answer: finally. The finally block always runs — it is where cleanup goes.
What is the difference between 'throw' and 'throws'?
- They are identical
- throw raises an exception now; throws declares a checked exception in a method signature
- throws raises an exception; throw declares it
- throw is for errors, throws is for warnings
Answer: throw raises an exception now; throws declares a checked exception in a method signature. throw is a statement that raises an exception; throws declares a checked exception to callers.
A checked exception extends which class (but not RuntimeException)?
- Throwable
- Error
- Exception
- Object
Answer: Exception. Checked exceptions extend Exception but not RuntimeException, so the compiler forces handling.
Which is the root class of the whole exception hierarchy?
- Exception
- Error
- Throwable
- RuntimeException
Answer: Throwable. Everything throwable descends from Throwable.
To create an UNCHECKED custom exception, you extend...
- Exception
- RuntimeException
- Error
- Throwable
Answer: RuntimeException. Extending RuntimeException makes an unchecked exception — no throws clause needed.
What does multi-catch 'catch (NumberFormatException | NullPointerException e)' do?
- Catches both types with the same handler
- Catches only the first type
- Is a compile error
- Catches every exception
Answer: Catches both types with the same handler. The | joins types so one block handles either exception the same way.
Which interface must a class implement to be used in try-with-resources?
- Closeable only
- AutoCloseable
- Disposable
- Resource
Answer: AutoCloseable. A resource in try(...) must implement AutoCloseable; its close() is called automatically.
Do you need a 'throws' clause for an unchecked (RuntimeException) exception?
- Yes, always
- No, it can be thrown without declaration
- Only in main()
- Only if caught
Answer: No, it can be thrown without declaration. Only checked exceptions require throws; unchecked ones can be thrown freely.
After try-with-resources, when is the resource's close() method called?
- Only if no exception is thrown
- Automatically when the block ends, even on an exception
- Never automatically
- Only inside finally
Answer: Automatically when the block ends, even on an exception. close() runs automatically at the end of the block whether it ends normally or via an exception.
How do you read the original cause of a wrapped/chained exception?
- e.getMessage()
- e.getCause()
- e.getClass()
- e.getOrigin()
Answer: e.getCause(). getCause() returns the original exception passed as the chaining cause.
Continue this course
- Previous: Previous Lesson
- Next: Next Lesson