Definition
trywraps code that might throw an exception. If an exception occurs inside the try block, execution jumps to the matching catch block.catchhandles a specific exception type. Multiple catch blocks can follow a single try, each handling a different exception.finallycontains cleanup code that runs regardless of whether an exception occurred. It always executes except forSystem.exit()or JVM crash.throwcreates and raises an exception object immediately:throw new IllegalArgumentException("msg").throwsdeclares in the method signature which checked exceptions the method might produce:void read() throws IOException.- Without exception handling, a single error crashes the entire system, leaks resources, and leaves data in an inconsistent state.
Exception handling is the emergency protocol at National Bank of India. try means "attempt this transaction." catch means "if it fails, execute this recovery plan." finally means "always lock the vault at closing time." throw means "raise the alarm because something went wrong." throws means "warning: this procedure may trigger an alarm." Without emergency protocols, one bounced cheque shuts down the entire bank. With them, the bounced cheque is handled, Priya is notified, and the bank continues operating.
Understanding the Concepts
What is exception handling?
Exception handling is Java's built-in mechanism for managing runtime errors gracefully. Instead of crashing the program when something goes wrong, you detect the error, take corrective action (retry, fallback, log, alert), and either continue or fail cleanly.
Java provides five keywords for exception handling: try, catch, finally, throw, and throws. Together they let you write code that handles failures at the right level of abstraction.
package com.bank.service;
import java.math.BigDecimal;
public class ExceptionHandlingOverview {
// throws: DECLARES that this method might throw a checked exception
public static String transfer(String from, String to, BigDecimal amount)
throws TransferException {
// throw: CREATES and RAISES an exception
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Amount must be positive: " + amount);
}
// try: wraps code that might fail
try {
debit(from, amount);
credit(to, amount);
return "UTR" + System.currentTimeMillis();
// catch: handles specific exception types
} catch (RuntimeException e) {
throw new TransferException(
"Transfer failed: " + from + " to " + to, e);
// finally: cleanup that ALWAYS runs
} finally {
System.out.println("Audit: transfer attempt " + from + " -> " + to);
}
}
private static void debit(String account, BigDecimal amount) {
System.out.println("Debited Rs." + amount + " from " + account);
}
private static void credit(String account, BigDecimal amount) {
System.out.println("Credited Rs." + amount + " to " + account);
}
public static void main(String[] args) {
try {
String utr = transfer("SAV2001", "CUR3001", new BigDecimal("15000"));
System.out.println("Success. UTR: " + utr);
} catch (TransferException e) {
System.out.println("Failed: " + e.getMessage());
}
}
}
class TransferException extends Exception {
public TransferException(String message, Throwable cause) {
super(message, cause);
}
}
Debited Rs.15000 from SAV2001
Credited Rs.15000 to CUR3001
Audit: transfer attempt SAV2001 -> CUR3001
Success. UTR: UTR1703123456789
Why do we need exception handling?
Without exception handling, a failure at any step means: data corruption (money debited but not credited), resource leaks (database connections never closed), no user feedback (blank screen), and cascading failures (all subsequent requests fail).
package com.bank.demo;
import java.math.BigDecimal;
public class WithoutExceptionHandling {
// WITHOUT exception handling: program crashes
public static void unsafeTransfer() {
System.out.println("Step 1: Debit SAV2001 Rs.50000");
// Step 1 succeeds.
System.out.println("Step 2: Credit CUR3001 Rs.50000");
throw new RuntimeException("Database connection lost!");
// Step 2 fails. But Step 1 already executed.
// Money is debited but NEVER credited. Rs.50000 LOST.
// Connection never closed. Pool drains after 100 failures.
// No error message to customer. Blank screen.
}
// WITH exception handling: graceful recovery
public static void safeTransfer() {
try {
System.out.println("Step 1: Debit SAV2001 Rs.50000");
System.out.println("Step 2: Credit CUR3001 Rs.50000");
throw new RuntimeException("Database connection lost!");
} catch (RuntimeException e) {
System.out.println("Rolling back debit...");
System.out.println("Customer message: Transfer failed, please retry.");
} finally {
System.out.println("Connection closed.");
}
}
public static void main(String[] args) {
System.out.println("=== Safe transfer ===");
safeTransfer();
}
}
=== Safe transfer ===
Step 1: Debit SAV2001 Rs.50000
Step 2: Credit CUR3001 Rs.50000
Rolling back debit...
Customer message: Transfer failed, please retry.
Connection closed.
How does the try keyword work?
The try block wraps code that might throw an exception. If no exception occurs, the catch block is skipped and execution continues after the try-catch. If an exception occurs, execution jumps immediately to the first matching catch block.
package com.bank.demo;
import java.math.BigDecimal;
public class TryBlockDemo {
public static void main(String[] args) {
try {
System.out.println("Before risky operation");
BigDecimal result = new BigDecimal("50000").divide(BigDecimal.ZERO);
System.out.println("This line never executes: " + result);
} catch (ArithmeticException e) {
System.out.println("Caught: " + e.getMessage());
}
System.out.println("Program continues after try-catch");
}
}
Before risky operation
Caught: Division by zero
Program continues after try-catch
How does the catch keyword work?
Each catch block handles a specific exception type. You can have multiple catch blocks for different exception types, ordered from most specific to most general.
package com.bank.demo;
import java.io.IOException;
import java.math.BigDecimal;
public class CatchBlockDemo {
public static void processTransaction(int scenarioId) throws IOException {
switch (scenarioId) {
case 1 -> throw new IllegalArgumentException("Bad amount");
case 2 -> throw new IllegalStateException("Account frozen");
case 3 -> throw new IOException("File not found");
}
System.out.println("Transaction processed");
}
public static void main(String[] args) {
for (int i = 1; i <= 4; i++) {
try {
processTransaction(i);
} catch (IllegalArgumentException e) {
System.out.println("Bad input: " + e.getMessage());
} catch (IllegalStateException e) {
System.out.println("Wrong state: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO error: " + e.getMessage());
}
}
}
}
Bad input: Bad amount
Wrong state: Account frozen
IO error: File not found
Transaction processed
How does the finally keyword work?
The finally block runs after try and catch regardless of whether an exception occurred. It is guaranteed to execute in normal circumstances, making it the correct place for resource cleanup.
package com.bank.demo;
public class FinallyBlockDemo {
public static void main(String[] args) {
System.out.println("=== No exception ===");
try {
System.out.println("Try: success");
} finally {
System.out.println("Finally: always runs");
}
System.out.println("\n=== With exception ===");
try {
System.out.println("Try: about to fail");
throw new RuntimeException("Something broke");
} catch (RuntimeException e) {
System.out.println("Catch: " + e.getMessage());
} finally {
System.out.println("Finally: still runs after exception");
}
}
}
=== No exception ===
Try: success
Finally: always runs
=== With exception ===
Try: about to fail
Catch: Something broke
Finally: still runs after exception
How do throw and throws work?
The throw keyword creates and raises an exception inside the method body. The throws keyword declares in the method signature which checked exceptions the method might produce.
package com.bank.service;
import java.math.BigDecimal;
public class ThrowAndThrowsDemo {
// throws: declares in the signature
public static BigDecimal calculateEMI(BigDecimal principal, double ratePercent, int months)
throws InvalidLoanException {
// throw: raises immediately for invalid input
if (principal.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Principal must be positive");
}
// throw: raises for business rule violation
if (months > 360) {
throw new InvalidLoanException("Loan tenure cannot exceed 360 months");
}
double rate = ratePercent / 12 / 100;
double emi = principal.doubleValue() * rate * Math.pow(1 + rate, months)
/ (Math.pow(1 + rate, months) - 1);
return BigDecimal.valueOf(emi).setScale(2, java.math.RoundingMode.HALF_UP);
}
public static void main(String[] args) {
try {
BigDecimal emi = calculateEMI(new BigDecimal("1000000"), 8.5, 240);
System.out.println("Monthly EMI: Rs." + emi);
} catch (InvalidLoanException e) {
System.out.println("Loan error: " + e.getMessage());
}
}
}
class InvalidLoanException extends Exception {
public InvalidLoanException(String message) {
super(message);
}
}
Monthly EMI: Rs.8678.23
What is the modern approach to exception handling?
Modern Java uses try-with-resources (Java 7+) for automatic resource cleanup, unchecked exceptions for most error conditions, and a global exception handler for centralized error management.
package com.bank.demo;
import java.io.BufferedReader;
import java.io.StringReader;
public class ModernExceptionHandling {
public static void main(String[] args) {
// Modern: try-with-resources auto-closes resources
String csvData = "SAV2001,50000\nCUR3001,75000";
try (BufferedReader reader = new BufferedReader(new StringReader(csvData))) {
String line;
while ((line = reader.readLine()) != null) {
String[] parts = line.split(",");
System.out.println("Account: " + parts[0] + ", Balance: Rs." + parts[1]);
}
} catch (Exception e) {
System.out.println("Error reading data: " + e.getMessage());
}
// reader.close() called AUTOMATICALLY. No finally needed.
System.out.println("\nModern pattern: unchecked exceptions + global handler");
System.out.println("Service: throws domain exceptions (unchecked)");
System.out.println("Controller: ZERO try-catch");
System.out.println("@ControllerAdvice: catches and maps to HTTP responses");
}
}
Account: SAV2001, Balance: Rs.50000
Account: CUR3001, Balance: Rs.75000
Modern pattern: unchecked exceptions + global handler
Service: throws domain exceptions (unchecked)
Controller: ZERO try-catch
@ControllerAdvice: catches and maps to HTTP responses
What are common mistakes with the five keywords?
package com.bank.demo;
public class FiveKeywordMistakes {
// WRONG: empty catch block (swallowing exceptions)
public static void wrongSwallow() {
try {
throw new RuntimeException("Important error!");
} catch (Exception e) {
// Empty. Error disappears silently.
}
System.out.println("Wrong: error swallowed, no evidence");
}
// CORRECT: log and rethrow or handle
public static void correctHandle() {
try {
throw new RuntimeException("Important error!");
} catch (Exception e) {
System.out.println("Logged: " + e.getMessage());
// In production: log.error("msg", e); throw new ServiceException("msg", e);
}
}
// WRONG: returning from finally
public static int wrongFinallyReturn() {
try {
return 1;
} finally {
return 2; // Overrides try's return value!
}
}
public static void main(String[] args) {
wrongSwallow();
correctHandle();
System.out.println("Finally return gotcha: " + wrongFinallyReturn());
}
}
Wrong: error swallowed, no evidence
Logged: Important error!
Finally return gotcha: 2
What are common interview follow-up questions?
- Can you have try without catch? (Yes, try-finally is valid. Also try-with-resources without catch.)
- Can you have try without catch or finally? (No, try must have at least one catch or a finally.)
- What order are multiple catch blocks evaluated? (Top to bottom, first match wins.)
- Can finally change the return value? (Yes, return in finally overrides try/catch return.)
- What is try-with-resources? (Automatic cleanup for AutoCloseable resources, Java 7+.)
Visual Overview
┌─────────────────────────────────────────────┐
│ try block │
│ Code that might throw an exception │
│ debit(account, amount); │
│ credit(toAccount, amount); │
└───────┬───────────────────┬─────────────────┘
│ │
no exception exception thrown
│ │
│ ┌──────▼──────────────┐
│ │ catch block │
│ │ Handle the error │
│ │ Rollback, log, retry│
│ └──────┬──────────────┘
│ │
└─────────┬─────────┘
│
┌──────▼──────────────┐
│ finally block │
│ Cleanup: close conn │
│ Always runs │
└──────┬──────────────┘
│
▼
Continue execution
Key Takeaway
Exception handling is Java's mechanism for managing runtime errors using five keywords. try wraps code that might throw an exception. catch handles specific exception types with recovery logic. finally provides guaranteed cleanup that runs regardless of success or failure. throw creates and raises an exception object immediately. throws declares in the method signature which checked exceptions the method might produce.
Without exception handling, a failure in the credit step of a fund transfer at National Bank of India means Rs.50,000 is debited from SAV2001 but never credited to CUR3001, the database connection is never closed, the connection pool drains after 100 failures, and every subsequent customer sees a blank screen. With exception handling, the failed credit triggers a catch that rolls back the debit, try-with-resources auto-closes the connection, Priya gets a clear "Transfer failed, please retry" message, and the system continues serving other customers.
Modern Java exception handling uses try-with-resources instead of finally for resource cleanup, unchecked domain exceptions instead of checked ones for cleaner APIs, and centralized exception handlers like @ControllerAdvice instead of per-method try-catch blocks.
Remember one sentence: try attempts the risky operation, catch handles the failure, finally cleans up no matter what, throw raises the alarm, and throws warns the caller that an alarm might sound.
---