Xử lý Ngoại lệ trong Java

Exception handling giúp chương trình xử lý lỗi một cách graceful thay vì crash. Hãy cùng tìm hiểu try-catch blocks và custom exceptions!

1. Try-Catch Cơ bản

Cú pháp cơ bản để bắt và xử lý exceptions:

public class ExceptionExample {
    public static void main(String[] args) {
        try {
            int result = 10 / 0; // ArithmeticException
            System.out.println(result);
        } catch (ArithmeticException e) {
            System.out.println("Không thể chia cho 0!");
            e.printStackTrace();
        }
        
        System.out.println("Chương trình tiếp tục...");
    }
}

2. Multiple Catch Blocks

Bắt nhiều loại exception khác nhau:

public void readFile(String filename) {
    try {
        FileReader file = new FileReader(filename);
        BufferedReader reader = new BufferedReader(file);
        
        String line = reader.readLine();
        int number = Integer.parseInt(line);
        
        reader.close();
    } catch (FileNotFoundException e) {
        System.out.println("File không tồn tại: " + filename);
    } catch (IOException e) {
        System.out.println("Lỗi đọc file: " + e.getMessage());
    } catch (NumberFormatException e) {
        System.out.println("Dữ liệu không phải số!");
    } catch (Exception e) {
        // Catch-all cho các exception khác
        System.out.println("Lỗi không xác định: " + e);
    }
}

// Java 7+: Multi-catch
try {
    // some code
} catch (IOException | SQLException e) {
    System.out.println("Lỗi IO hoặc SQL: " + e);
}

3. Finally Block

Code trong finally luôn chạy, bất kể có exception hay không:

public void processFile(String filename) {
    FileReader file = null;
    try {
        file = new FileReader(filename);
        // Process file...
    } catch (IOException e) {
        System.out.println("Lỗi: " + e.getMessage());
    } finally {
        // Luôn chạy - cleanup resources
        if (file != null) {
            try {
                file.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Finally block executed");
    }
}

// Try-with-resources (Java 7+) - tốt hơn
public void processFileModern(String filename) {
    try (FileReader file = new FileReader(filename);
         BufferedReader reader = new BufferedReader(file)) {
        
        // Process...
        // Tự động close() trong finally
        
    } catch (IOException e) {
        System.out.println("Lỗi: " + e.getMessage());
    }
}

4. Checked vs Unchecked Exceptions

// Checked Exception - phải xử lý hoặc throws
public void readFile(String path) throws IOException {
    FileReader file = new FileReader(path); // IOException
    // ...
}

// Unchecked Exception - không bắt buộc xử lý
public void divide(int a, int b) {
    int result = a / b; // ArithmeticException (unchecked)
}

// Common checked exceptions:
// - IOException
// - SQLException
// - ClassNotFoundException

// Common unchecked exceptions:
// - NullPointerException
// - ArrayIndexOutOfBoundsException
// - IllegalArgumentException
// - ArithmeticException

5. Throws và Throw

Ném exception lên method gọi nó:

// throws - khai báo method có thể ném exception
public void validateAge(int age) throws IllegalArgumentException {
    if (age < 0 || age > 150) {
        // throw - ném exception
        throw new IllegalArgumentException("Tuổi không hợp lệ: " + age);
    }
}

// Sử dụng
try {
    validateAge(-5);
} catch (IllegalArgumentException e) {
    System.out.println(e.getMessage());
}

// Có thể throws nhiều exceptions
public void complexOperation() 
        throws IOException, SQLException, ClassNotFoundException {
    // Method implementation
}

6. Custom Exceptions

Tạo exception class riêng:

// Custom checked exception
public class InvalidAgeException extends Exception {
    public InvalidAgeException(String message) {
        super(message);
    }
    
    public InvalidAgeException(String message, Throwable cause) {
        super(message, cause);
    }
}

// Custom unchecked exception
public class InsufficientFundsException extends RuntimeException {
    private double amount;
    
    public InsufficientFundsException(double amount) {
        super("Không đủ tiền: " + amount);
        this.amount = amount;
    }
    
    public double getAmount() {
        return amount;
    }
}

// Sử dụng
public class BankAccount {
    private double balance;
    
    public void withdraw(double amount) throws InsufficientFundsException {
        if (amount > balance) {
            throw new InsufficientFundsException(amount - balance);
        }
        balance -= amount;
    }
}

// Main
try {
    account.withdraw(1000);
} catch (InsufficientFundsException e) {
    System.out.println("Thiếu: " + e.getAmount());
}

7. Best Practices

// ❌ BAD - Swallowing exception
try {
    riskyOperation();
} catch (Exception e) {
    // Không làm gì - rất nguy hiểm!
}

// ✅ GOOD - Proper handling
try {
    riskyOperation();
} catch (SpecificException e) {
    logger.error("Operation failed", e);
    // Handle hoặc rethrow
    throw new BusinessException("Cannot process", e);
}

Kết luận

Exception handling là kỹ năng quan trọng trong Java: