Difference between using Throwable and Exception in a try catch
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Throwable catches everything: both Exception (recoverable problems) and Error (fatal JVM-level problems like OutOfMemoryError). Exception catches only the recoverable subset. In nearly all application code, you should catch Exception (or better, specific exception types). Catching Throwable is reserved for top-level error handlers, framework shutdown hooks, and thread pool executors where you genuinely need to prevent silent thread death.
The Java Exception Hierarchy
Understanding what to catch requires knowing the class hierarchy:
The catch clause matches the specified type and all of its subclasses. Catching Throwable matches the entire tree. Catching Exception matches everything except the Error branch.
Catching Exception: The Standard Practice
Catching Exception covers all problems that application code is expected to handle: checked exceptions (I/O failures, network errors, parse errors) and unchecked runtime exceptions (null references, illegal arguments, array bounds violations).
The pattern above catches the specific expected exception first (IOException), then uses a broader Exception catch as a safety net. This is the recommended approach for most application code.
Catching Throwable: When and Why
Catching Throwable includes Error subtypes. These represent conditions the JVM considers unrecoverable:
| Error Type | Meaning | Should you catch it? |
OutOfMemoryError | JVM heap is exhausted | Almost never (state is corrupted) |
StackOverflowError | Call stack exceeded | Rarely (indicates infinite recursion) |
NoClassDefFoundError | Required class missing at runtime | Sometimes (classpath issues in plugins) |
VirtualMachineError | JVM itself is failing | Never (JVM is dying) |
AssertionError | Assertion failed | Rarely (indicates logic bugs) |
LinkageError | Class loading/linking failure | Sometimes (in classloader-heavy systems) |
There are legitimate reasons to catch Throwable, but they are narrow:
1. Top-Level Thread Pool Error Handlers
Without catching Throwable here, an OutOfMemoryError thrown inside the task kills the thread silently. The Future stores the error, but if nobody calls Future.get(), the failure is invisible.
2. Framework Shutdown Hooks
3. Plugin or Classloader Isolation Boundaries
Side-by-Side Comparison
| Aspect | catch (Throwable t) | catch (Exception e) |
| Catches checked exceptions | Yes | Yes |
| Catches runtime exceptions | Yes | Yes |
Catches Error subtypes | Yes | No |
| Typical use case | Framework-level error handling | Application-level error handling |
| Recovery expectation | Low (log and fail) | High (handle and continue) |
| Risk of masking fatal errors | High | Low |
| Best practice recommendation | Only at thread/process boundaries | Preferred for business logic |
The "Catch Specific" Principle
Both catch (Throwable) and catch (Exception) are broad catches. In most cases, you should catch the most specific exception type you expect.
Multi-Catch Syntax (Java 7+)
Java 7 introduced multi-catch, which is cleaner than catching a broad type when you want to handle several specific exceptions the same way.
This is preferable to catch (Exception e) because it explicitly documents which exceptions you expect, and it does not accidentally catch exceptions you did not anticipate.
What Happens If You Catch Throwable and An Error Occurs
When the JVM throws an OutOfMemoryError, catching it does not magically free memory. The application's state is likely corrupted: collections may be partially populated, transactions may be half-committed, and locks may be held indefinitely. Catching the error prevents the thread from terminating, but the application is in an unreliable state.
The safe pattern for OutOfMemoryError is to catch it only at the outermost level (e.g., in a thread pool's error handler), log it, and shut down the application or reject the current request.
Common Pitfalls
- Catching
Throwablein application code and swallowing the error. This masks fatal JVM problems likeOutOfMemoryError, making them invisible. The application continues in a corrupt state, producing wrong results or deadlocking. Always re-throw or terminate after catchingThrowable. - Catching
Exceptionand silently returning null. Even recoverable exceptions should be logged. Returning null silently pushes the failure downstream, where it appears as aNullPointerExceptionwith no indication of the root cause. - Catching
Errorto "recover" from it.StackOverflowErrorandOutOfMemoryErrorindicate that fundamental resources are exhausted. Recovery code is likely to trigger the same error again. Catch these only for logging and shutdown. - Using
catch (Exception e)when you know the specific exceptions. Broad catches accidentally swallow unexpected exceptions, hiding bugs. Catch the narrowest type possible. - Forgetting that
catchclauses are evaluated in order. If you catchExceptionbefore a specific subtype, the specific catch block is unreachable. The compiler will flag this, but it is a common mistake during refactoring. - Not including the original exception in the re-thrown exception. Always chain exceptions:
throw new ServiceException("message", e). Losing the cause makes debugging nearly impossible.
Summary
Catch Exception in application code where you expect to recover from failures. Catch Throwable only at thread boundaries, shutdown hooks, or framework-level error handlers where silent thread death would be worse than catching a potentially fatal error. In both cases, prefer catching the most specific exception types first. Never silently swallow exceptions of any type. When you do catch Throwable, treat it as a last-resort safety net: log the error and either re-throw it or initiate a controlled shutdown. The key principle is that catching broader types increases the risk of masking bugs, so default to the narrowest catch that handles your known failure modes.

