6.10.6: Handling Multiple Exceptions: The Vending Machine Example
Ever watched someone get frustrated at a vending machine? In practice, they insert their money, make their selection, and nothing happens. Now, then they try again. And again. Maybe the machine ate their dollar. Maybe the snack is stuck. In practice, maybe they picked the wrong button. The point is — a lot of things can go wrong, and each problem needs a different solution.
That's exactly what we're talking about when we discuss handling multiple exceptions in programming. Real applications don't just fail in one way. They fail in dozens of ways, and your code needs to handle each one intelligently.
What Is Handling Multiple Exceptions?
When something goes wrong in a program, Java (and most other languages) throws an exception — basically, it stops what it's doing and signals that an error occurred. The problem is, different errors require different responses.
Think about a vending machine. If you try to buy a soda but:
- You haven't inserted any money → the machine should ask for payment
- You inserted $1 but the soda costs $1.50 →the machine should tell you you're short
- The soda is out of stock →the machine should say it's unavailable
- The machine's refrigeration unit broke →the machine should display an error code
Same action — hitting the buy button — but four completely different problems. Each one needs its own response.
Handling multiple exceptions means writing code that can distinguish between these different error types and respond appropriately to each. Instead of one generic "something went wrong" message, you get specific, useful feedback.
The Multi-Catch Syntax
Modern Java lets you handle multiple exception types in a single try-catch block using the multi-catch syntax. It looks like this:
try {
// attempt to dispense product
} catch (InsufficientFundsException | OutOfStockException e) {
// handle either exception the same way
} catch (MachineMalfunctionException e) {
// handle this one differently
}
The pipe symbol (|) means "or". So that first catch block handles either an InsufficientFundsException OR an OutOfStockException. Clean, readable, and efficient.
Why It Matters
Here's the thing — if you don't handle multiple exceptions properly, your app either crashes with a scary stack trace or gives users useless error messages. Neither option is acceptable in real software.
Let me paint a picture. You're building an app that processes credit card payments. Three things could go wrong:
- The card is expired
- The card has insufficient funds
- The payment gateway is down
If you catch all three with one generic exception handler, you can't tell the user what actually happened. "An error occurred" is infuriating when you're trying to buy something and you have no idea if you should try again, call your bank, or update your payment method.
But with proper multiple exception handling, you can say exactly what went wrong and what to do about it. That's the difference between a professional application and something that feels broken.
How It Works: The Vending Machine Example
Let's build this out properly. Here's a simplified vending machine class that demonstrates multiple exception handling:
public class VendingMachine {
public void purchase(String item, double moneyInserted)
throws InsufficientFundsException,
OutOfStockException,
MachineMalfunctionException {
Product product = getProduct(item);
// Check if product exists
if (product == null) {
throw new MachineMalfunctionException("Product not found");
}
// Check availability
if (product.getQuantity() == 0) {
throw new OutOfStockException(item + " is out of stock");
}
// Check payment
if (moneyInserted < product.getPrice()) {
throw new InsufficientFundsException(
"Need $" + (product.getPrice() - moneyInserted) + " more");
}
// Dispense
product.dispense();
}
}
Now, here's how you'd handle these different exceptions when someone makes a purchase:
VendingMachine machine = new VendingMachine();
try {
machine.So naturally, out. Plus, println("Sorry: " + e. println("Payment issue: " + e.println("Enjoy your chips!out.50);
System.Practically speaking, println("Please insert more money. getMessage());
System.");
} catch (OutOfStockException e) {
System.Now, out. ");
} catch (MachineMalfunctionException e) {
System.println("Would you like a different item?out.Now, purchase("Chips", 1. out.Also, getMessage());
System. ");
} catch (InsufficientFundsException e) {
System.out.
See how each exception gets its own handling? The user gets specific guidance based on what actually went wrong.
### Ordering Your Catch Blocks
Here's something that trips up beginners: the order of your catch blocks matters.
Java evaluates them top to bottom. Consider this: if you put a general exception type first, it'll catch everything and your more specific handlers will never run. It's like putting a "catch all errors" rule before your specific error rules — the specific ones never get a chance.
This is where a lot of people lose the thread.
The rule is simple: catch the most specific exceptions first, then the general ones. In our vending machine example, all three exceptions are at the same level of specificity, so the order between them doesn't matter. But if you were also catching a general `Exception` type, it would have to come last.
### The Multi-Catch Alternative
Sometimes you have multiple exceptions that need identical handling. Instead of writing separate catch blocks, you can combine them:
```java
try {
machine.purchase("Chips", 1.50);
} catch (InsufficientFundsException | OutOfStockException e) {
// Both of these are "customer-facing" issues
System.out.println("Transaction failed: " + e.getMessage());
System.out.println("Please try again or select a different item.");
} catch (MachineMalfunctionException e) {
// This is a technical issue
System.out.println("Please contact support.");
}
This keeps your code DRY (Don't Repeat Yourself) when the response is the same for different error types Surprisingly effective..
Common Mistakes What People Get Wrong
The biggest mistake I see is catching Exception or Throwable at the top level and calling it a day. Yes, your program won't crash. But you'll have no idea what actually went wrong, and neither will your users.
Another issue: swallowing exceptions silently. This happens when you catch an exception and do nothing with it:
try {
// something risky
} catch (Exception e) {
// nothing here - the exception just disappears
}
This is dangerous because problems get hidden. Something failed, but nobody knows why, and the program continues as if nothing happened. So at minimum, log the error. Even better — handle it properly.
People also forget that exceptions can wrap other exceptions. A MachineMalfunctionException might contain the original IOException that caused it. Use getCause() to dig down to the root issue when you need to Which is the point..
Practical Tips What Actually Works
Here's my advice after years of writing exception handling code:
1. Catch what you can handle. Don't catch exceptions just because you can. Catch them when you know what to do with them. If you can't meaningfully respond to an exception, let it propagate up to code that can Most people skip this — try not to..
2. Log at the right level. Technical exceptions (like database connection failures) should be logged with full stack traces for your development team. User-facing exceptions should have friendly messages. Don't mix these up.
3. Use specific exception types. Don't just throw generic Exception. Create custom exception classes that clearly communicate what went wrong. InsufficientFundsException tells you more than PaymentException, which tells you more than RuntimeException.
4. Test your error paths. Everyone tests the happy path — when everything works. But you need to test what happens when things fail too. Force each exception to happen and verify the right message appears Most people skip this — try not to..
5. Keep exception handling separate from business logic. Don't clutter your main code with try-catch blocks if you can avoid it. Consider using a centralized error-handling approach, especially in larger applications And that's really what it comes down to..
FAQ
What's the difference between multi-catch and separate catch blocks?
Multi-catch (catch (Exception1 | Exception2 e)) combines multiple exception types into one handler when they need identical treatment. Separate catch blocks are better when each exception requires different handling logic Most people skip this — try not to..
Can I catch exceptions in any order?
Not exactly. You must catch more specific exceptions before more general ones. If you catch Exception first, it'll swallow all your specific exception handlers and they'll never execute Not complicated — just consistent..
Should I create custom exception classes?
Yes, especially for domain-specific errors. A VendingMachineException hierarchy with InsufficientFundsException and OutOfStockException as subclasses communicates intent much better than generic Java exceptions.
What happens if an exception isn't caught?
If no catch block handles an exception, it propagates up the call stack. If it reaches the top without being caught, your program terminates and prints a stack trace to the console — not ideal for production software Worth knowing..
Can I have multiple exceptions in a single try block?
Absolutely. That's the whole point. A single try block can throw dozens of different exceptions, and you handle each one appropriately in separate catch blocks Turns out it matters..
The Bottom Line
Exception handling isn't about preventing errors — errors will happen regardless. A well-handled exception feels like a helpful assistant. Which means it's about responding to them gracefully. A poorly-handled one feels like a broken program.
The vending machine analogy works because we've all been there — staring at a machine that won't give us what we paid for, wishing it would just tell us why. Your users deserve that same clarity. Handle your exceptions specifically, and they'll thank you for it.