Try-Catch-Finally Exception Handling in Java
Java Try-Catch-Finally
Exception handling in Java is accomplished using try-catch-finally blocks, which allow you to gracefully handle runtime errors and ensure proper resource cleanup. This mechanism provides a structured way to deal with exceptional conditions that may occur during program execution.
Basic Try-Catch Syntax
The try-catch statement allows you to handle exceptions that may occur in your code:
public class BasicExceptionHandling {
public static void main(String[] args) {
// Basic try-catch structure
try {
// Code that may throw an exception
int result = 10 / 0; // ArithmeticException
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
// Handle the specific exception
System.out.println("Error: Division by zero!");
System.out.println("Exception message: " + e.getMessage());
}
System.out.println("Program continues after exception handling");
// Array index out of bounds example
try {
int[] numbers = {1, 2, 3};
System.out.println(numbers[5]); // ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Error: Array index out of bounds!");
System.out.println("Invalid index accessed");
}
// Null pointer exception example
try {
String text = null;
int length = text.length(); // NullPointerException
} catch (NullPointerException e) {
System.out.println("Error: Null pointer exception!");
System.out.println("Attempted to call method on null object");
}
}
}
Multiple Catch Blocks
You can handle different types of exceptions with multiple catch blocks:
import java.io.*;
import java.util.Scanner;
public class MultipleCatchBlocks {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("Enter a number: ");
String input = scanner.nextLine();
// Parse the input to integer
int number = Integer.parseInt(input); // NumberFormatException possible
// Perform division
int result = 100 / number; // ArithmeticException possible
System.out.println("100 / " + number + " = " + result);
// Array access
int[] array = {1, 2, 3, 4, 5};
System.out.println("Array element at index " + number + ": " + array[number]);
} catch (NumberFormatException e) {
System.out.println("Error: Invalid number format!");
System.out.println("Please enter a valid integer");
} catch (ArithmeticException e) {
System.out.println("Error: Cannot divide by zero!");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Error: Array index out of bounds!");
System.out.println("Valid indices are 0-4");
} catch (Exception e) {
// Generic catch block for any other exceptions
System.out.println("An unexpected error occurred: " + e.getMessage());
}
scanner.close();
}
}
Multi-Catch (Java 7+)
Handle multiple exception types in a single catch block:
import java.io.*;
import java.nio.file.*;
public class MultiCatch {
public static void main(String[] args) {
// Multi-catch syntax
try {
// Operations that can throw different exceptions
String content = Files.readString(Paths.get("nonexistent.txt"));
int number = Integer.parseInt(content);
int result = 100 / number;
} catch (IOException | NumberFormatException | ArithmeticException e) {
// Handle multiple exception types in one block
System.out.println("Error occurred: " + e.getClass().getSimpleName());
System.out.println("Message: " + e.getMessage());
// Type-specific handling within multi-catch
if (e instanceof IOException) {
System.out.println("File operation failed");
} else if (e instanceof NumberFormatException) {
System.out.println("Invalid number format in file");
} else if (e instanceof ArithmeticException) {
System.out.println("Mathematical error occurred");
}
}
// Example with different exception handling strategies
handleFileOperations();
}
public static void handleFileOperations() {
try {
// File operations
Path path = Paths.get("data.txt");
String content = Files.readString(path);
// String operations
String[] parts = content.split(",");
int value = Integer.parseInt(parts[0].trim());
} catch (IOException | SecurityException e) {
// File-related exceptions
System.err.println("File access error: " + e.getMessage());
logError("FILE_ERROR", e);
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
// Data parsing exceptions
System.err.println("Data format error: " + e.getMessage());
logError("DATA_ERROR", e);
}
}
private static void logError(String errorType, Exception e) {
System.out.println("Logging " + errorType + ": " + e.getClass().getSimpleName());
}
}
Finally Block
The finally block executes regardless of whether an exception occurs:
import java.io.*;
public class FinallyBlock {
public static void main(String[] args) {
// Basic finally usage
try {
int result = performCalculation(10, 0);
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Calculation error: " + e.getMessage());
} finally {
System.out.println("This always executes - cleanup operations");
}
// File handling with finally
demonstrateFileHandling();
// Database connection example
demonstrateDatabaseConnection();
}
public static int performCalculation(int a, int b) {
return a / b;
}
public static void demonstrateFileHandling() {
FileInputStream file = null;
try {
file = new FileInputStream("data.txt");
// Read file operations
System.out.println("File opened successfully");
// Simulate some file operations
int data = file.read();
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getMessage());
} catch (IOException e) {
System.out.println("I/O error: " + e.getMessage());
} finally {
// Cleanup: Always close the file
if (file != null) {
try {
file.close();
System.out.println("File closed in finally block");
} catch (IOException e) {
System.out.println("Error closing file: " + e.getMessage());
}
}
}
}
public static void demonstrateDatabaseConnection() {
Connection connection = null;
try {
connection = openDatabaseConnection();
performDatabaseOperations(connection);
} catch (SQLException e) {
System.out.println("Database error: " + e.getMessage());
} finally {
// Always close database connection
if (connection != null) {
try {
connection.close();
System.out.println("Database connection closed");
} catch (SQLException e) {
System.out.println("Error closing connection: " + e.getMessage());
}
}
}
}
// Mock database classes for example
static class Connection {
public void close() throws SQLException {
System.out.println("Closing database connection");
}
}
static class SQLException extends Exception {
public SQLException(String message) {
super(message);
}
}
private static Connection openDatabaseConnection() {
return new Connection();
}
private static void performDatabaseOperations(Connection conn) {
System.out.println("Performing database operations");
}
}
Try-with-Resources (Java 7+)
Automatic resource management for classes implementing AutoCloseable:
import java.io.*;
import java.nio.file.*;
import java.util.Scanner;
public class TryWithResources {
public static void main(String[] args) {
// Basic try-with-resources
try (FileInputStream fis = new FileInputStream("input.txt");
FileOutputStream fos = new FileOutputStream("output.txt")) {
// File operations
int data;
while ((data = fis.read()) != -1) {
fos.write(data);
}
System.out.println("File copied successfully");
} catch (IOException e) {
System.out.println("I/O error: " + e.getMessage());
}
// Resources are automatically closed here
// Multiple resources
copyFileWithBuffering();
// Scanner with try-with-resources
readUserInput();
// Custom resource example
useCustomResource();
}
public static void copyFileWithBuffering() {
try (FileInputStream fis = new FileInputStream("source.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("destination.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
System.out.println("Buffered copy completed");
} catch (IOException e) {
System.out.println("Copy error: " + e.getMessage());
}
}
public static void readUserInput() {
try (Scanner scanner = new Scanner(System.in)) {
System.out.print("Enter your name: ");
String name = scanner.nextLine();
System.out.println("Hello, " + name + "!");
} catch (Exception e) {
System.out.println("Input error: " + e.getMessage());
}
}
public static void useCustomResource() {
try (CustomResource resource = new CustomResource("MyResource")) {
resource.doSomething();
System.out.println("Working with custom resource");
} catch (Exception e) {
System.out.println("Resource error: " + e.getMessage());
}
}
// Custom resource class implementing AutoCloseable
static class CustomResource implements AutoCloseable {
private String name;
private boolean closed = false;
public CustomResource(String name) {
this.name = name;
System.out.println("Opening resource: " + name);
}
public void doSomething() {
if (closed) {
throw new IllegalStateException("Resource is closed");
}
System.out.println("Using resource: " + name);
}
@Override
public void close() throws Exception {
if (!closed) {
System.out.println("Closing resource: " + name);
closed = true;
}
}
}
}
Exception Information and Stack Traces
Getting detailed information about exceptions:
import java.io.*;
import java.util.Arrays;
public class ExceptionInformation {
public static void main(String[] args) {
try {
methodA();
} catch (Exception e) {
// Print exception information
System.out.println("Exception Type: " + e.getClass().getName());
System.out.println("Exception Message: " + e.getMessage());
System.out.println("Exception Cause: " + e.getCause());
// Print stack trace
System.out.println("\nStack Trace:");
e.printStackTrace();
// Get stack trace as array
StackTraceElement[] stackTrace = e.getStackTrace();
System.out.println("\nStack Trace Elements:");
for (StackTraceElement element : stackTrace) {
System.out.println(" " + element.getClassName() + "." +
element.getMethodName() + "(" +
element.getFileName() + ":" +
element.getLineNumber() + ")");
}
}
// Demonstrate different ways to log exceptions
demonstrateExceptionLogging();
}
public static void methodA() throws Exception {
methodB();
}
public static void methodB() throws Exception {
methodC();
}
public static void methodC() throws Exception {
throw new IllegalArgumentException("Something went wrong in methodC");
}
public static void demonstrateExceptionLogging() {
try {
riskyOperation();
} catch (Exception e) {
// Log with different levels of detail
logException("ERROR", e);
logExceptionWithContext("CRITICAL", e, "During user registration");
}
}
public static void riskyOperation() throws IOException {
throw new IOException("Network connection failed");
}
public static void logException(String level, Exception e) {
System.out.println("[" + level + "] " + e.getClass().getSimpleName() +
": " + e.getMessage());
}
public static void logExceptionWithContext(String level, Exception e, String context) {
System.out.println("[" + level + "] " + context);
System.out.println(" Exception: " + e.getClass().getSimpleName());
System.out.println(" Message: " + e.getMessage());
System.out.println(" Location: " + e.getStackTrace()[0]);
}
}
Nested Try-Catch Blocks
Sometimes you need nested exception handling:
import java.io.*;
import java.util.zip.*;
public class NestedTryCatch {
public static void main(String[] args) {
// Nested try-catch for complex operations
try {
// Outer try block
processCompressedFile("data.zip");
} catch (IOException e) {
System.out.println("I/O Error: " + e.getMessage());
// Try to recover or log additional information
try {
logErrorToFile(e);
} catch (IOException logError) {
System.err.println("Failed to log error: " + logError.getMessage());
// As last resort, print to console
e.printStackTrace();
}
}
// Database transaction example
performDatabaseTransaction();
}
public static void processCompressedFile(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename);
ZipInputStream zis = new ZipInputStream(fis)) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
try {
// Process each entry
processZipEntry(zis, entry);
} catch (NumberFormatException e) {
System.out.println("Skipping invalid entry: " + entry.getName());
System.out.println("Reason: " + e.getMessage());
// Continue with next entry
} catch (IllegalArgumentException e) {
System.out.println("Invalid data in entry: " + entry.getName());
// Log but continue processing
} finally {
zis.closeEntry();
}
}
}
}
public static void processZipEntry(ZipInputStream zis, ZipEntry entry) throws IOException {
System.out.println("Processing: " + entry.getName());
// Read and parse entry data
byte[] buffer = new byte[1024];
int bytesRead = zis.read(buffer);
if (bytesRead > 0) {
String content = new String(buffer, 0, bytesRead);
// Parse content - may throw NumberFormatException
int value = Integer.parseInt(content.trim());
// Validate - may throw IllegalArgumentException
if (value < 0) {
throw new IllegalArgumentException("Value must be positive: " + value);
}
System.out.println("Processed value: " + value);
}
}
public static void logErrorToFile(Exception e) throws IOException {
try (PrintWriter writer = new PrintWriter(new FileWriter("error.log", true))) {
writer.println("Error occurred at: " + System.currentTimeMillis());
writer.println("Exception: " + e.getClass().getName());
writer.println("Message: " + e.getMessage());
e.printStackTrace(writer);
writer.println("---");
}
}
public static void performDatabaseTransaction() {
DatabaseConnection conn = null;
DatabaseTransaction transaction = null;
try {
conn = new DatabaseConnection();
conn.connect();
try {
transaction = conn.beginTransaction();
// Perform multiple database operations
transaction.executeUpdate("INSERT INTO users ...");
transaction.executeUpdate("UPDATE accounts ...");
transaction.executeUpdate("INSERT INTO logs ...");
// Commit if all operations succeed
transaction.commit();
System.out.println("Transaction completed successfully");
} catch (SQLException e) {
System.out.println("Database operation failed: " + e.getMessage());
if (transaction != null) {
try {
transaction.rollback();
System.out.println("Transaction rolled back");
} catch (SQLException rollbackError) {
System.err.println("Rollback failed: " + rollbackError.getMessage());
}
}
} finally {
if (transaction != null) {
try {
transaction.close();
} catch (SQLException e) {
System.err.println("Error closing transaction: " + e.getMessage());
}
}
}
} catch (SQLException e) {
System.out.println("Connection error: " + e.getMessage());
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
System.err.println("Error closing connection: " + e.getMessage());
}
}
}
}
// Mock database classes for example
static class DatabaseConnection {
public void connect() throws SQLException { }
public void close() throws SQLException { }
public DatabaseTransaction beginTransaction() throws SQLException {
return new DatabaseTransaction();
}
}
static class DatabaseTransaction {
public void executeUpdate(String sql) throws SQLException { }
public void commit() throws SQLException { }
public void rollback() throws SQLException { }
public void close() throws SQLException { }
}
static class SQLException extends Exception {
public SQLException(String message) { super(message); }
}
}
Best Practices
1. Catch Specific Exceptions
public class SpecificExceptionHandling {
public void goodPractice() {
try {
// Some risky operation
performFileOperation();
} catch (FileNotFoundException e) {
// Handle file not found specifically
System.out.println("File not found: " + e.getMessage());
createDefaultFile();
} catch (SecurityException e) {
// Handle security issues specifically
System.out.println("Access denied: " + e.getMessage());
requestPermissions();
} catch (IOException e) {
// Handle other I/O issues
System.out.println("I/O error: " + e.getMessage());
useAlternativeMethod();
}
}
public void badPractice() {
try {
performFileOperation();
} catch (Exception e) {
// Too generic - loses specific error information
System.out.println("Something went wrong");
}
}
private void performFileOperation() throws IOException { }
private void createDefaultFile() { }
private void requestPermissions() { }
private void useAlternativeMethod() { }
}
2. Don't Swallow Exceptions
public class ExceptionSwallowing {
public void badPractice() {
try {
riskyOperation();
} catch (Exception e) {
// Bad: Silently ignoring exception
}
}
public void goodPractice() {
try {
riskyOperation();
} catch (Exception e) {
// Good: At minimum, log the exception
System.err.println("Operation failed: " + e.getMessage());
e.printStackTrace();
// Even better: Take appropriate action
handleFailure(e);
}
}
private void riskyOperation() throws Exception { }
private void handleFailure(Exception e) { }
}
3. Use Try-with-Resources for AutoCloseable
public class ResourceManagement {
public void oldWay() {
FileInputStream fis = null;
try {
fis = new FileInputStream("file.txt");
// Use file
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
System.out.println("Error closing file: " + e.getMessage());
}
}
}
}
public void newWay() {
try (FileInputStream fis = new FileInputStream("file.txt")) {
// Use file
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
// File automatically closed
}
}
4. Provide Meaningful Error Messages
public class MeaningfulErrors {
public void validateUser(String username, int age) {
try {
if (username == null || username.trim().isEmpty()) {
throw new IllegalArgumentException("Username cannot be null or empty");
}
if (age < 0 || age > 150) {
throw new IllegalArgumentException(
"Age must be between 0 and 150, but was: " + age);
}
// Process user
processUser(username, age);
} catch (IllegalArgumentException e) {
System.out.println("Validation error: " + e.getMessage());
System.out.println("Please check your input and try again");
}
}
private void processUser(String username, int age) { }
}
Summary
Exception handling with try-catch-finally provides:
- Error Recovery: Handle exceptions gracefully without crashing
- Resource Management: Ensure proper cleanup with finally blocks
- Automatic Cleanup: Use try-with-resources for AutoCloseable objects
- Multiple Exception Types: Handle different exceptions appropriately
- Debugging Information: Access stack traces and exception details
Key Points:
- Try Block: Contains code that may throw exceptions
- Catch Block: Handles specific exception types
- Finally Block: Always executes for cleanup
- Try-with-Resources: Automatic resource management
- Multi-Catch: Handle multiple exception types together
Best Practices:
- Catch specific exceptions rather than generic Exception
- Don't swallow exceptions silently
- Use try-with-resources for resource management
- Provide meaningful error messages and logging
- Clean up resources in finally blocks when not using try-with-resources
Proper exception handling makes your applications more robust, maintainable, and user-friendly by gracefully handling error conditions.