异常处理是Java程序设计中的重要概念,它提供了一种优雅地处理程序运行时错误的机制。本章将详细介绍Java异常处理的概念、语法和最佳实践。

6.1 异常基础概念

6.1.1 什么是异常

/**
 * 异常演示类 - 展示常见的异常情况
 */
public class ExceptionDemo {
    public static void main(String[] args) {
        System.out.println("=== 常见异常演示 ===");
        
        // 1. 算术异常 (ArithmeticException)
        demonstrateArithmeticException();
        
        // 2. 空指针异常 (NullPointerException)
        demonstrateNullPointerException();
        
        // 3. 数组越界异常 (ArrayIndexOutOfBoundsException)
        demonstrateArrayIndexOutOfBoundsException();
        
        // 4. 类型转换异常 (ClassCastException)
        demonstrateClassCastException();
        
        // 5. 数字格式异常 (NumberFormatException)
        demonstrateNumberFormatException();
    }
    
    public static void demonstrateArithmeticException() {
        System.out.println("\n--- 算术异常演示 ---");
        try {
            int a = 10;
            int b = 0;
            int result = a / b;  // 除零异常
            System.out.println("结果: " + result);
        } catch (ArithmeticException e) {
            System.out.println("捕获到算术异常: " + e.getMessage());
            System.out.println("异常类型: " + e.getClass().getSimpleName());
        }
    }
    
    public static void demonstrateNullPointerException() {
        System.out.println("\n--- 空指针异常演示 ---");
        try {
            String str = null;
            int length = str.length();  // 空指针异常
            System.out.println("字符串长度: " + length);
        } catch (NullPointerException e) {
            System.out.println("捕获到空指针异常: " + e.getMessage());
            System.out.println("建议: 在使用对象前检查是否为null");
        }
    }
    
    public static void demonstrateArrayIndexOutOfBoundsException() {
        System.out.println("\n--- 数组越界异常演示 ---");
        try {
            int[] array = {1, 2, 3, 4, 5};
            int value = array[10];  // 数组越界
            System.out.println("数组值: " + value);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("捕获到数组越界异常: " + e.getMessage());
            System.out.println("数组长度检查很重要!");
        }
    }
    
    public static void demonstrateClassCastException() {
        System.out.println("\n--- 类型转换异常演示 ---");
        try {
            Object obj = "这是一个字符串";
            Integer number = (Integer) obj;  // 类型转换异常
            System.out.println("转换后的数字: " + number);
        } catch (ClassCastException e) {
            System.out.println("捕获到类型转换异常: " + e.getMessage());
            System.out.println("建议: 使用instanceof检查类型");
        }
    }
    
    public static void demonstrateNumberFormatException() {
        System.out.println("\n--- 数字格式异常演示 ---");
        try {
            String invalidNumber = "abc123";
            int number = Integer.parseInt(invalidNumber);  // 数字格式异常
            System.out.println("解析后的数字: " + number);
        } catch (NumberFormatException e) {
            System.out.println("捕获到数字格式异常: " + e.getMessage());
            System.out.println("无法将 '" + "abc123" + "' 转换为数字");
        }
    }
}

6.1.2 异常的层次结构

/**
 * 异常层次结构演示
 */
public class ExceptionHierarchyDemo {
    public static void main(String[] args) {
        System.out.println("=== Java异常层次结构 ===");
        
        // Throwable
        //   ├── Error (系统级错误,通常不处理)
        //   │   ├── OutOfMemoryError
        //   │   ├── StackOverflowError
        //   │   └── VirtualMachineError
        //   └── Exception (可处理的异常)
        //       ├── RuntimeException (运行时异常,非检查异常)
        //       │   ├── NullPointerException
        //       │   ├── ArrayIndexOutOfBoundsException
        //       │   ├── ClassCastException
        //       │   └── IllegalArgumentException
        //       └── 检查异常 (Checked Exceptions)
        //           ├── IOException
        //           ├── SQLException
        //           ├── ClassNotFoundException
        //           └── ParseException
        
        demonstrateExceptionTypes();
    }
    
    public static void demonstrateExceptionTypes() {
        System.out.println("\n=== 异常类型演示 ===");
        
        // 1. 运行时异常(非检查异常)
        System.out.println("\n1. 运行时异常(RuntimeException):");
        try {
            // 这些异常在编译时不需要强制处理
            throwRuntimeException();
        } catch (RuntimeException e) {
            System.out.println("捕获运行时异常: " + e.getClass().getSimpleName());
            System.out.println("消息: " + e.getMessage());
        }
        
        // 2. 检查异常
        System.out.println("\n2. 检查异常(Checked Exception):");
        try {
            // 这些异常在编译时必须处理
            throwCheckedException();
        } catch (Exception e) {
            System.out.println("捕获检查异常: " + e.getClass().getSimpleName());
            System.out.println("消息: " + e.getMessage());
        }
        
        // 3. 错误(Error)
        System.out.println("\n3. 错误(Error):");
        System.out.println("Error通常表示严重的系统级问题,一般不应该被捕获");
        System.out.println("例如: OutOfMemoryError, StackOverflowError");
    }
    
    public static void throwRuntimeException() {
        // 运行时异常示例
        throw new IllegalArgumentException("这是一个非法参数异常");
    }
    
    public static void throwCheckedException() throws Exception {
        // 检查异常示例
        throw new Exception("这是一个检查异常");
    }
}

6.2 异常处理语法

6.2.1 try-catch语句

import java.io.*;
import java.util.Scanner;

/**
 * try-catch语句详细演示
 */
public class TryCatchDemo {
    public static void main(String[] args) {
        System.out.println("=== try-catch语句演示 ===");
        
        // 1. 基本try-catch
        basicTryCatch();
        
        // 2. 多个catch块
        multipleCatchBlocks();
        
        // 3. catch块的顺序
        catchBlockOrder();
        
        // 4. try-catch-finally
        tryCatchFinally();
        
        // 5. try-with-resources
        tryWithResources();
    }
    
    public static void basicTryCatch() {
        System.out.println("\n--- 基本try-catch ---");
        
        try {
            System.out.println("尝试执行可能出错的代码");
            int result = 10 / 0;  // 这里会抛出异常
            System.out.println("这行代码不会执行");
        } catch (ArithmeticException e) {
            System.out.println("捕获到异常,进行处理");
            System.out.println("异常信息: " + e.getMessage());
        }
        
        System.out.println("程序继续执行");
    }
    
    public static void multipleCatchBlocks() {
        System.out.println("\n--- 多个catch块 ---");
        
        String[] testCases = {null, "abc", "5"};
        
        for (int i = 0; i < testCases.length; i++) {
            try {
                System.out.println("\n测试用例 " + (i + 1) + ": " + testCases[i]);
                
                // 可能抛出NullPointerException
                int length = testCases[i].length();
                System.out.println("字符串长度: " + length);
                
                // 可能抛出NumberFormatException
                int number = Integer.parseInt(testCases[i]);
                System.out.println("解析的数字: " + number);
                
                // 可能抛出ArithmeticException
                int result = 100 / number;
                System.out.println("计算结果: " + result);
                
            } catch (NullPointerException e) {
                System.out.println("处理空指针异常: 字符串为null");
            } catch (NumberFormatException e) {
                System.out.println("处理数字格式异常: 无法解析为数字");
            } catch (ArithmeticException e) {
                System.out.println("处理算术异常: 除零错误");
            }
        }
    }
    
    public static void catchBlockOrder() {
        System.out.println("\n--- catch块的顺序 ---");
        
        try {
            // 模拟抛出不同类型的异常
            throw new IllegalArgumentException("非法参数异常");
            
        } catch (IllegalArgumentException e) {
            // 具体异常类型应该放在前面
            System.out.println("捕获具体异常: " + e.getClass().getSimpleName());
            System.out.println("消息: " + e.getMessage());
            
        } catch (RuntimeException e) {
            // 父类异常放在后面
            System.out.println("捕获运行时异常: " + e.getClass().getSimpleName());
            
        } catch (Exception e) {
            // 最通用的异常放在最后
            System.out.println("捕获通用异常: " + e.getClass().getSimpleName());
        }
        
        System.out.println("\n注意: catch块的顺序很重要!");
        System.out.println("- 子类异常必须在父类异常之前");
        System.out.println("- 否则会导致编译错误");
    }
    
    public static void tryCatchFinally() {
        System.out.println("\n--- try-catch-finally ---");
        
        FileInputStream fis = null;
        try {
            System.out.println("尝试打开文件");
            fis = new FileInputStream("nonexistent.txt");
            System.out.println("文件打开成功");
            
        } catch (FileNotFoundException e) {
            System.out.println("文件未找到: " + e.getMessage());
            
        } finally {
            // finally块总是会执行
            System.out.println("执行清理工作");
            if (fis != null) {
                try {
                    fis.close();
                    System.out.println("文件流已关闭");
                } catch (IOException e) {
                    System.out.println("关闭文件流时出错: " + e.getMessage());
                }
            }
        }
        
        System.out.println("\nfinally块的特点:");
        System.out.println("- 无论是否发生异常都会执行");
        System.out.println("- 常用于资源清理");
        System.out.println("- 即使在try或catch中有return语句,finally也会执行");
    }
    
    public static void tryWithResources() {
        System.out.println("\n--- try-with-resources ---");
        
        // Java 7引入的语法,自动管理资源
        try (Scanner scanner = new Scanner(System.in);
             FileWriter writer = new FileWriter("temp.txt")) {
            
            System.out.println("使用try-with-resources自动管理资源");
            writer.write("这是测试内容");
            System.out.println("文件写入完成");
            
        } catch (IOException e) {
            System.out.println("IO异常: " + e.getMessage());
        }
        // 资源会自动关闭,无需手动调用close()
        
        System.out.println("\ntry-with-resources的优点:");
        System.out.println("- 自动关闭实现了AutoCloseable接口的资源");
        System.out.println("- 代码更简洁,减少资源泄漏风险");
        System.out.println("- 即使发生异常,资源也会被正确关闭");
    }
}

6.2.2 throws和throw关键字

import java.io.*;
import java.util.Date;

/**
 * throws和throw关键字演示
 */
public class ThrowsAndThrowDemo {
    public static void main(String[] args) {
        System.out.println("=== throws和throw关键字演示 ===");
        
        // 1. throws关键字演示
        demonstrateThrows();
        
        // 2. throw关键字演示
        demonstrateThrow();
        
        // 3. 异常传播演示
        demonstrateExceptionPropagation();
    }
    
    public static void demonstrateThrows() {
        System.out.println("\n--- throws关键字演示 ---");
        
        try {
            // 调用声明抛出异常的方法
            readFile("test.txt");
        } catch (IOException e) {
            System.out.println("处理IO异常: " + e.getMessage());
        }
        
        try {
            parseNumber("abc");
        } catch (NumberFormatException e) {
            System.out.println("处理数字格式异常: " + e.getMessage());
        }
    }
    
    // 使用throws声明方法可能抛出的检查异常
    public static void readFile(String filename) throws IOException {
        System.out.println("尝试读取文件: " + filename);
        
        // 这个方法可能抛出IOException
        FileReader reader = new FileReader(filename);
        
        // 如果到达这里,说明文件存在
        System.out.println("文件读取成功");
        reader.close();
    }
    
    // throws可以声明多个异常
    public static void parseNumber(String str) throws NumberFormatException, IllegalArgumentException {
        if (str == null) {
            throw new IllegalArgumentException("输入字符串不能为null");
        }
        
        // 这里可能抛出NumberFormatException
        int number = Integer.parseInt(str);
        System.out.println("解析成功: " + number);
    }
    
    public static void demonstrateThrow() {
        System.out.println("\n--- throw关键字演示 ---");
        
        try {
            validateAge(-5);
        } catch (IllegalArgumentException e) {
            System.out.println("年龄验证失败: " + e.getMessage());
        }
        
        try {
            validateEmail("invalid-email");
        } catch (IllegalArgumentException e) {
            System.out.println("邮箱验证失败: " + e.getMessage());
        }
        
        try {
            divide(10, 0);
        } catch (ArithmeticException e) {
            System.out.println("除法运算失败: " + e.getMessage());
        }
    }
    
    // 使用throw主动抛出异常
    public static void validateAge(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("年龄不能为负数: " + age);
        }
        if (age > 150) {
            throw new IllegalArgumentException("年龄不能超过150岁: " + age);
        }
        System.out.println("年龄验证通过: " + age);
    }
    
    public static void validateEmail(String email) {
        if (email == null || email.trim().isEmpty()) {
            throw new IllegalArgumentException("邮箱地址不能为空");
        }
        if (!email.contains("@")) {
            throw new IllegalArgumentException("邮箱地址格式不正确: " + email);
        }
        System.out.println("邮箱验证通过: " + email);
    }
    
    public static double divide(double a, double b) {
        if (b == 0) {
            throw new ArithmeticException("除数不能为零");
        }
        return a / b;
    }
    
    public static void demonstrateExceptionPropagation() {
        System.out.println("\n--- 异常传播演示 ---");
        
        try {
            methodA();
        } catch (Exception e) {
            System.out.println("在main方法中捕获异常: " + e.getMessage());
            System.out.println("异常传播路径:");
            
            // 打印异常堆栈跟踪
            StackTraceElement[] stackTrace = e.getStackTrace();
            for (int i = 0; i < Math.min(stackTrace.length, 5); i++) {
                StackTraceElement element = stackTrace[i];
                System.out.println("  " + element.getMethodName() + "() 在 " + 
                                 element.getFileName() + ":" + element.getLineNumber());
            }
        }
    }
    
    public static void methodA() throws Exception {
        System.out.println("进入methodA");
        methodB();
        System.out.println("这行代码不会执行");
    }
    
    public static void methodB() throws Exception {
        System.out.println("进入methodB");
        methodC();
        System.out.println("这行代码不会执行");
    }
    
    public static void methodC() throws Exception {
        System.out.println("进入methodC");
        throw new Exception("在methodC中抛出的异常");
    }
}

6.3 自定义异常

6.3.1 创建自定义异常类

/**
 * 银行业务异常 - 自定义检查异常
 */
public class BankException extends Exception {
    private String errorCode;
    private double amount;
    
    public BankException(String message) {
        super(message);
    }
    
    public BankException(String message, String errorCode) {
        super(message);
        this.errorCode = errorCode;
    }
    
    public BankException(String message, String errorCode, double amount) {
        super(message);
        this.errorCode = errorCode;
        this.amount = amount;
    }
    
    public BankException(String message, Throwable cause) {
        super(message, cause);
    }
    
    public BankException(String message, String errorCode, Throwable cause) {
        super(message, cause);
        this.errorCode = errorCode;
    }
    
    public String getErrorCode() {
        return errorCode;
    }
    
    public double getAmount() {
        return amount;
    }
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getSimpleName()).append(": ");
        sb.append(getMessage());
        
        if (errorCode != null) {
            sb.append(" [错误代码: ").append(errorCode).append("]");
        }
        
        if (amount != 0) {
            sb.append(" [涉及金额: ").append(amount).append("]");
        }
        
        return sb.toString();
    }
}
/**
 * 余额不足异常 - 继承自BankException
 */
public class InsufficientFundsException extends BankException {
    private double currentBalance;
    private double requestedAmount;
    
    public InsufficientFundsException(double currentBalance, double requestedAmount) {
        super(String.format("余额不足:当前余额 %.2f,请求金额 %.2f", 
                          currentBalance, requestedAmount), 
              "INSUFFICIENT_FUNDS", requestedAmount);
        this.currentBalance = currentBalance;
        this.requestedAmount = requestedAmount;
    }
    
    public double getCurrentBalance() {
        return currentBalance;
    }
    
    public double getRequestedAmount() {
        return requestedAmount;
    }
    
    public double getShortfall() {
        return requestedAmount - currentBalance;
    }
}
/**
 * 账户锁定异常 - 自定义运行时异常
 */
public class AccountLockedException extends RuntimeException {
    private String accountNumber;
    private String lockReason;
    private Date lockTime;
    
    public AccountLockedException(String accountNumber, String lockReason) {
        super("账户已锁定:" + accountNumber + ",原因:" + lockReason);
        this.accountNumber = accountNumber;
        this.lockReason = lockReason;
        this.lockTime = new Date();
    }
    
    public String getAccountNumber() {
        return accountNumber;
    }
    
    public String getLockReason() {
        return lockReason;
    }
    
    public Date getLockTime() {
        return lockTime;
    }
}
/**
 * 无效交易异常
 */
public class InvalidTransactionException extends BankException {
    public enum TransactionType {
        DEPOSIT("存款"),
        WITHDRAWAL("取款"),
        TRANSFER("转账"),
        PAYMENT("支付");
        
        private final String description;
        
        TransactionType(String description) {
            this.description = description;
        }
        
        public String getDescription() {
            return description;
        }
    }
    
    private TransactionType transactionType;
    private String transactionId;
    
    public InvalidTransactionException(String message, TransactionType type, String transactionId) {
        super(message, "INVALID_TRANSACTION");
        this.transactionType = type;
        this.transactionId = transactionId;
    }
    
    public TransactionType getTransactionType() {
        return transactionType;
    }
    
    public String getTransactionId() {
        return transactionId;
    }
    
    @Override
    public String toString() {
        return super.toString() + 
               " [交易类型: " + transactionType.getDescription() + 
               ", 交易ID: " + transactionId + "]";
    }
}

6.3.2 使用自定义异常

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 银行账户类 - 使用自定义异常
 */
public class BankAccount {
    private String accountNumber;
    private String ownerName;
    private double balance;
    private boolean isLocked;
    private int failedAttempts;
    private static final int MAX_FAILED_ATTEMPTS = 3;
    
    // 交易历史
    private Map<String, Transaction> transactionHistory;
    
    public BankAccount(String accountNumber, String ownerName, double initialBalance) {
        this.accountNumber = accountNumber;
        this.ownerName = ownerName;
        this.balance = initialBalance;
        this.isLocked = false;
        this.failedAttempts = 0;
        this.transactionHistory = new HashMap<>();
    }
    
    // 存款
    public void deposit(double amount) throws InvalidTransactionException {
        String transactionId = generateTransactionId();
        
        try {
            validateAccount();
            validateAmount(amount, "存款金额必须大于0");
            
            balance += amount;
            recordTransaction(transactionId, InvalidTransactionException.TransactionType.DEPOSIT, amount);
            
            System.out.println(String.format("存款成功:%.2f,当前余额:%.2f", amount, balance));
            
        } catch (Exception e) {
            throw new InvalidTransactionException(
                "存款失败:" + e.getMessage(),
                InvalidTransactionException.TransactionType.DEPOSIT,
                transactionId
            );
        }
    }
    
    // 取款
    public void withdraw(double amount) throws BankException {
        String transactionId = generateTransactionId();
        
        validateAccount();
        validateAmount(amount, "取款金额必须大于0");
        
        if (amount > balance) {
            throw new InsufficientFundsException(balance, amount);
        }
        
        balance -= amount;
        recordTransaction(transactionId, InvalidTransactionException.TransactionType.WITHDRAWAL, amount);
        
        System.out.println(String.format("取款成功:%.2f,当前余额:%.2f", amount, balance));
    }
    
    // 转账
    public void transfer(BankAccount targetAccount, double amount) throws BankException {
        String transactionId = generateTransactionId();
        
        try {
            validateAccount();
            targetAccount.validateAccount();
            validateAmount(amount, "转账金额必须大于0");
            
            if (amount > balance) {
                throw new InsufficientFundsException(balance, amount);
            }
            
            // 执行转账
            this.balance -= amount;
            targetAccount.balance += amount;
            
            // 记录交易
            recordTransaction(transactionId, InvalidTransactionException.TransactionType.TRANSFER, amount);
            targetAccount.recordTransaction(transactionId, InvalidTransactionException.TransactionType.TRANSFER, amount);
            
            System.out.println(String.format("转账成功:从 %s 转给 %s,金额:%.2f", 
                             this.ownerName, targetAccount.ownerName, amount));
            
        } catch (BankException e) {
            throw e; // 重新抛出银行异常
        } catch (Exception e) {
            throw new InvalidTransactionException(
                "转账失败:" + e.getMessage(),
                InvalidTransactionException.TransactionType.TRANSFER,
                transactionId
            );
        }
    }
    
    // 验证账户状态
    private void validateAccount() {
        if (isLocked) {
            throw new AccountLockedException(accountNumber, "账户因多次操作失败被锁定");
        }
    }
    
    // 验证金额
    private void validateAmount(double amount, String errorMessage) throws InvalidTransactionException {
        if (amount <= 0) {
            failedAttempts++;
            if (failedAttempts >= MAX_FAILED_ATTEMPTS) {
                isLocked = true;
                throw new AccountLockedException(accountNumber, "多次无效操作");
            }
            throw new InvalidTransactionException(
                errorMessage,
                InvalidTransactionException.TransactionType.DEPOSIT,
                generateTransactionId()
            );
        }
        
        // 重置失败次数
        failedAttempts = 0;
    }
    
    // 记录交易
    private void recordTransaction(String transactionId, 
                                 InvalidTransactionException.TransactionType type, 
                                 double amount) {
        Transaction transaction = new Transaction(transactionId, type, amount, new Date());
        transactionHistory.put(transactionId, transaction);
    }
    
    // 生成交易ID
    private String generateTransactionId() {
        return "TXN-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
    }
    
    // 解锁账户(管理员操作)
    public void unlockAccount() {
        this.isLocked = false;
        this.failedAttempts = 0;
        System.out.println("账户 " + accountNumber + " 已解锁");
    }
    
    // 获取账户信息
    public void printAccountInfo() {
        System.out.println("=== 账户信息 ===");
        System.out.println("账号:" + accountNumber);
        System.out.println("户主:" + ownerName);
        System.out.println("余额:" + String.format("%.2f", balance));
        System.out.println("状态:" + (isLocked ? "锁定" : "正常"));
        System.out.println("失败次数:" + failedAttempts);
        System.out.println("交易记录数:" + transactionHistory.size());
    }
    
    // Getter方法
    public String getAccountNumber() {
        return accountNumber;
    }
    
    public String getOwnerName() {
        return ownerName;
    }
    
    public double getBalance() {
        return balance;
    }
    
    public boolean isLocked() {
        return isLocked;
    }
    
    // 内部类:交易记录
    private static class Transaction {
        private String id;
        private InvalidTransactionException.TransactionType type;
        private double amount;
        private Date timestamp;
        
        public Transaction(String id, InvalidTransactionException.TransactionType type, 
                         double amount, Date timestamp) {
            this.id = id;
            this.type = type;
            this.amount = amount;
            this.timestamp = timestamp;
        }
        
        @Override
        public String toString() {
            return String.format("%s: %s %.2f at %s", 
                               id, type.getDescription(), amount, timestamp);
        }
    }
}

6.3.3 自定义异常的综合应用

/**
 * 自定义异常综合演示
 */
public class CustomExceptionDemo {
    public static void main(String[] args) {
        System.out.println("=== 自定义异常综合演示 ===");
        
        // 创建银行账户
        BankAccount account1 = new BankAccount("ACC001", "张三", 1000.0);
        BankAccount account2 = new BankAccount("ACC002", "李四", 500.0);
        
        // 演示各种异常情况
        demonstrateValidOperations(account1, account2);
        demonstrateInsufficientFunds(account1);
        demonstrateInvalidTransactions(account1);
        demonstrateAccountLocking(account1);
        demonstrateExceptionChaining();
    }
    
    public static void demonstrateValidOperations(BankAccount account1, BankAccount account2) {
        System.out.println("\n--- 正常操作演示 ---");
        
        try {
            account1.printAccountInfo();
            
            account1.deposit(200.0);
            account1.withdraw(150.0);
            account1.transfer(account2, 100.0);
            
            System.out.println("\n操作完成后的账户状态:");
            account1.printAccountInfo();
            account2.printAccountInfo();
            
        } catch (BankException e) {
            System.out.println("银行业务异常: " + e);
        }
    }
    
    public static void demonstrateInsufficientFunds(BankAccount account) {
        System.out.println("\n--- 余额不足异常演示 ---");
        
        try {
            account.withdraw(2000.0);  // 余额不足
            
        } catch (InsufficientFundsException e) {
            System.out.println("捕获余额不足异常:");
            System.out.println("  异常信息: " + e.getMessage());
            System.out.println("  错误代码: " + e.getErrorCode());
            System.out.println("  当前余额: " + e.getCurrentBalance());
            System.out.println("  请求金额: " + e.getRequestedAmount());
            System.out.println("  缺少金额: " + e.getShortfall());
            
        } catch (BankException e) {
            System.out.println("其他银行异常: " + e);
        }
    }
    
    public static void demonstrateInvalidTransactions(BankAccount account) {
        System.out.println("\n--- 无效交易异常演示 ---");
        
        // 测试无效金额
        try {
            account.deposit(-100.0);  // 负数金额
            
        } catch (InvalidTransactionException e) {
            System.out.println("捕获无效交易异常:");
            System.out.println("  " + e);
            System.out.println("  交易类型: " + e.getTransactionType().getDescription());
            System.out.println("  交易ID: " + e.getTransactionId());
        }
        
        try {
            account.withdraw(0);  // 零金额
            
        } catch (InvalidTransactionException e) {
            System.out.println("\n再次捕获无效交易异常:");
            System.out.println("  " + e);
        } catch (BankException e) {
            System.out.println("其他银行异常: " + e);
        }
    }
    
    public static void demonstrateAccountLocking(BankAccount account) {
        System.out.println("\n--- 账户锁定异常演示 ---");
        
        // 连续进行无效操作,触发账户锁定
        for (int i = 1; i <= 4; i++) {
            try {
                System.out.println("\n第 " + i + " 次无效操作:");
                account.deposit(-50.0);
                
            } catch (AccountLockedException e) {
                System.out.println("账户被锁定:");
                System.out.println("  账户号: " + e.getAccountNumber());
                System.out.println("  锁定原因: " + e.getLockReason());
                System.out.println("  锁定时间: " + e.getLockTime());
                break;
                
            } catch (InvalidTransactionException e) {
                System.out.println("无效交易: " + e.getMessage());
            }
        }
        
        // 尝试在锁定状态下操作
        try {
            System.out.println("\n尝试在锁定状态下存款:");
            account.deposit(100.0);
            
        } catch (AccountLockedException e) {
            System.out.println("操作失败,账户已锁定: " + e.getMessage());
        } catch (Exception e) {
            System.out.println("其他异常: " + e.getMessage());
        }
        
        // 解锁账户
        account.unlockAccount();
        
        try {
            System.out.println("\n解锁后尝试存款:");
            account.deposit(100.0);
            
        } catch (Exception e) {
            System.out.println("操作失败: " + e.getMessage());
        }
    }
    
    public static void demonstrateExceptionChaining() {
        System.out.println("\n--- 异常链演示 ---");
        
        try {
            simulateComplexOperation();
            
        } catch (BankException e) {
            System.out.println("捕获顶层异常: " + e.getMessage());
            
            // 检查异常链
            Throwable cause = e.getCause();
            int level = 1;
            while (cause != null) {
                System.out.println("  原因 " + level + ": " + cause.getClass().getSimpleName() + 
                                 " - " + cause.getMessage());
                cause = cause.getCause();
                level++;
            }
            
            System.out.println("\n完整堆栈跟踪:");
            e.printStackTrace();
        }
    }
    
    private static void simulateComplexOperation() throws BankException {
        try {
            // 模拟底层操作失败
            throw new RuntimeException("数据库连接失败");
            
        } catch (RuntimeException e) {
            // 包装为业务异常
            throw new BankException("银行系统暂时不可用,请稍后重试", "SYSTEM_ERROR", e);
        }
    }
}

6.4 异常处理最佳实践

6.4.1 异常处理原则

import java.io.*;
import java.util.logging.Logger;
import java.util.logging.Level;

/**
 * 异常处理最佳实践演示
 */
public class ExceptionBestPractices {
    private static final Logger logger = Logger.getLogger(ExceptionBestPractices.class.getName());
    
    public static void main(String[] args) {
        System.out.println("=== 异常处理最佳实践 ===");
        
        demonstrateBestPractices();
    }
    
    public static void demonstrateBestPractices() {
        // 1. 具体异常处理
        demonstrateSpecificExceptionHandling();
        
        // 2. 资源管理
        demonstrateResourceManagement();
        
        // 3. 异常日志记录
        demonstrateExceptionLogging();
        
        // 4. 异常转换
        demonstrateExceptionTranslation();
        
        // 5. 失败快速原则
        demonstrateFailFast();
    }
    
    // 最佳实践1: 捕获具体的异常类型
    public static void demonstrateSpecificExceptionHandling() {
        System.out.println("\n--- 具体异常处理 ---");
        
        // ❌ 不好的做法:捕获过于宽泛的异常
        try {
            riskyOperation();
        } catch (Exception e) {
            // 这样会捕获所有异常,包括不应该处理的
            System.out.println("捕获到异常(不推荐): " + e.getMessage());
        }
        
        // ✅ 好的做法:捕获具体的异常类型
        try {
            riskyOperation();
        } catch (FileNotFoundException e) {
            System.out.println("文件未找到,使用默认配置");
            // 具体的恢复逻辑
        } catch (IOException e) {
            System.out.println("IO错误,重试操作: " + e.getMessage());
            // 重试逻辑
        } catch (SecurityException e) {
            System.out.println("权限不足: " + e.getMessage());
            // 权限处理逻辑
        }
    }
    
    private static void riskyOperation() throws IOException {
        // 模拟可能抛出不同异常的操作
        throw new FileNotFoundException("配置文件未找到");
    }
    
    // 最佳实践2: 正确的资源管理
    public static void demonstrateResourceManagement() {
        System.out.println("\n--- 资源管理 ---");
        
        // ❌ 不好的做法:可能导致资源泄漏
        badResourceManagement();
        
        // ✅ 好的做法:使用try-with-resources
        goodResourceManagement();
    }
    
    private static void badResourceManagement() {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("test.txt");
            // 处理文件
        } catch (IOException e) {
            System.out.println("文件操作失败: " + e.getMessage());
            // 如果这里return或抛出异常,finally可能不会执行
            return;
        } finally {
            // 这种方式容易出错
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    System.out.println("关闭文件失败: " + e.getMessage());
                }
            }
        }
    }
    
    private static void goodResourceManagement() {
        // 使用try-with-resources自动管理资源
        try (FileInputStream fis = new FileInputStream("test.txt");
             BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
            
            // 处理文件
            String line = reader.readLine();
            System.out.println("读取内容: " + line);
            
        } catch (IOException e) {
            System.out.println("文件操作失败: " + e.getMessage());
        }
        // 资源会自动关闭
    }
    
    // 最佳实践3: 异常日志记录
    public static void demonstrateExceptionLogging() {
        System.out.println("\n--- 异常日志记录 ---");
        
        try {
            operationThatMightFail();
        } catch (Exception e) {
            // ✅ 记录详细的异常信息
            logger.log(Level.SEVERE, "操作失败", e);
            
            // ✅ 提供用户友好的错误信息
            System.out.println("操作暂时无法完成,请稍后重试");
            
            // ❌ 不要只打印堆栈跟踪而不处理
            // e.printStackTrace(); // 仅用于调试
        }
    }
    
    private static void operationThatMightFail() throws Exception {
        throw new Exception("模拟操作失败");
    }
    
    // 最佳实践4: 异常转换
    public static void demonstrateExceptionTranslation() {
        System.out.println("\n--- 异常转换 ---");
        
        try {
            businessOperation();
        } catch (BusinessException e) {
            System.out.println("业务异常: " + e.getMessage());
            System.out.println("错误代码: " + e.getErrorCode());
        }
    }
    
    // 将底层异常转换为业务异常
    private static void businessOperation() throws BusinessException {
        try {
            // 模拟底层操作
            throw new SQLException("数据库连接失败");
        } catch (SQLException e) {
            // 转换为业务异常,隐藏底层实现细节
            throw new BusinessException("用户数据获取失败", "USER_DATA_ERROR", e);
        }
    }
    
    // 最佳实践5: 失败快速原则
    public static void demonstrateFailFast() {
        System.out.println("\n--- 失败快速原则 ---");
        
        try {
            processUserData(null, -1);
        } catch (IllegalArgumentException e) {
            System.out.println("参数验证失败: " + e.getMessage());
        }
    }
    
    // ✅ 在方法开始时验证参数,快速失败
    private static void processUserData(String userData, int userId) {
        // 参数验证 - 失败快速
        if (userData == null) {
            throw new IllegalArgumentException("用户数据不能为null");
        }
        if (userData.trim().isEmpty()) {
            throw new IllegalArgumentException("用户数据不能为空");
        }
        if (userId <= 0) {
            throw new IllegalArgumentException("用户ID必须大于0");
        }
        
        // 实际处理逻辑
        System.out.println("处理用户数据: " + userData + ", ID: " + userId);
    }
    
    // 自定义业务异常
    private static class BusinessException extends Exception {
        private String errorCode;
        
        public BusinessException(String message, String errorCode, Throwable cause) {
            super(message, cause);
            this.errorCode = errorCode;
        }
        
        public String getErrorCode() {
            return errorCode;
        }
    }
    
    // 模拟SQL异常
    private static class SQLException extends Exception {
        public SQLException(String message) {
            super(message);
        }
    }
}

6.4.2 异常处理反模式

/**
 * 异常处理反模式演示 - 应该避免的做法
 */
public class ExceptionAntiPatterns {
    public static void main(String[] args) {
        System.out.println("=== 异常处理反模式(应避免的做法)===");
        
        demonstrateAntiPatterns();
    }
    
    public static void demonstrateAntiPatterns() {
        // 反模式1: 吞噬异常
        demonstrateSwallowingExceptions();
        
        // 反模式2: 过度使用异常
        demonstrateOverusingExceptions();
        
        // 反模式3: 异常作为控制流
        demonstrateExceptionsAsControlFlow();
        
        // 反模式4: 不恰当的异常信息
        demonstrateInappropriateExceptionMessages();
    }
    
    // 反模式1: 吞噬异常(静默忽略)
    public static void demonstrateSwallowingExceptions() {
        System.out.println("\n--- 反模式1: 吞噬异常 ---");
        
        // ❌ 非常糟糕的做法:完全忽略异常
        try {
            int result = 10 / 0;
        } catch (Exception e) {
            // 什么都不做 - 这是最糟糕的做法!
        }
        
        // ❌ 稍好但仍然不好:只打印但不处理
        try {
            int result = 10 / 0;
        } catch (Exception e) {
            e.printStackTrace(); // 只打印,没有实际处理
        }
        
        // ✅ 正确的做法:记录并适当处理
        try {
            int result = 10 / 0;
        } catch (ArithmeticException e) {
            System.out.println("算术运算错误,使用默认值: " + e.getMessage());
            // 提供默认值或其他恢复逻辑
            int result = 0;
        }
    }
    
    // 反模式2: 过度使用异常
    public static void demonstrateOverusingExceptions() {
        System.out.println("\n--- 反模式2: 过度使用异常 ---");
        
        // ❌ 不好的做法:为正常的业务逻辑使用异常
        try {
            String result = findUserBad("nonexistent");
            System.out.println("找到用户: " + result);
        } catch (UserNotFoundException e) {
            System.out.println("用户不存在(通过异常处理)");
        }
        
        // ✅ 好的做法:使用返回值处理正常的业务情况
        String result = findUserGood("nonexistent");
        if (result != null) {
            System.out.println("找到用户: " + result);
        } else {
            System.out.println("用户不存在(通过返回值判断)");
        }
    }
    
    // 不好的做法:用异常处理正常业务逻辑
    private static String findUserBad(String username) throws UserNotFoundException {
        if (!"admin".equals(username)) {
            throw new UserNotFoundException("用户不存在: " + username);
        }
        return "Admin User";
    }
    
    // 好的做法:用返回值处理正常情况
    private static String findUserGood(String username) {
        if ("admin".equals(username)) {
            return "Admin User";
        }
        return null; // 或者使用Optional<String>
    }
    
    // 反模式3: 异常作为控制流
    public static void demonstrateExceptionsAsControlFlow() {
        System.out.println("\n--- 反模式3: 异常作为控制流 ---");
        
        // ❌ 不好的做法:使用异常控制程序流程
        try {
            for (int i = 0; ; i++) {
                System.out.println("处理元素: " + getElementBad(i));
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组遍历完成(通过异常)");
        }
        
        // ✅ 好的做法:使用正常的控制结构
        String[] array = {"A", "B", "C"};
        for (int i = 0; i < array.length; i++) {
            System.out.println("处理元素: " + array[i]);
        }
        System.out.println("数组遍历完成(正常流程)");
    }
    
    private static String getElementBad(int index) {
        String[] array = {"A", "B", "C"};
        return array[index]; // 故意让它越界来控制循环
    }
    
    // 反模式4: 不恰当的异常信息
    public static void demonstrateInappropriateExceptionMessages() {
        System.out.println("\n--- 反模式4: 不恰当的异常信息 ---");
        
        try {
            validateUserInputBad("");
        } catch (Exception e) {
            System.out.println("捕获到异常: " + e.getMessage());
        }
        
        try {
            validateUserInputGood("");
        } catch (Exception e) {
            System.out.println("捕获到异常: " + e.getMessage());
        }
    }
    
    // ❌ 不好的做法:异常信息不明确
    private static void validateUserInputBad(String input) {
        if (input == null || input.trim().isEmpty()) {
            throw new RuntimeException("错误"); // 信息太模糊
        }
    }
    
    // ✅ 好的做法:提供清晰、有用的异常信息
    private static void validateUserInputGood(String input) {
        if (input == null) {
            throw new IllegalArgumentException("用户输入不能为null");
        }
        if (input.trim().isEmpty()) {
            throw new IllegalArgumentException("用户输入不能为空字符串");
        }
    }
    
    // 自定义异常
    private static class UserNotFoundException extends Exception {
        public UserNotFoundException(String message) {
            super(message);
        }
    }
}

本章小结

本章详细介绍了Java异常处理机制:

异常基础:

  • 异常是程序运行时发生的错误事件
  • Java异常层次结构:Throwable → Error/Exception → RuntimeException
  • 检查异常vs非检查异常的区别

异常处理语法:

  • try-catch:捕获和处理异常
  • finally:无论是否发生异常都会执行的代码块
  • try-with-resources:自动资源管理
  • throws:声明方法可能抛出的异常
  • throw:主动抛出异常

自定义异常:

  • 继承Exception(检查异常)或RuntimeException(非检查异常)
  • 提供有意义的异常信息和错误代码
  • 异常链:保留原始异常信息

最佳实践:

  • 捕获具体的异常类型
  • 正确管理资源
  • 记录异常日志
  • 异常转换和封装
  • 失败快速原则
  • 避免异常反模式

下一章预告

下一章我们将学习Java集合框架,包括List、Set、Map等常用集合类的使用方法和特点。

练习题

  1. 创建一个文件处理系统,包含自定义异常和完整的异常处理机制
  2. 设计一个用户注册系统,使用异常处理验证用户输入
  3. 实现一个简单的计算器,处理各种可能的异常情况
  4. 创建一个网络请求模拟器,演示异常链和异常转换的使用