跳转至

异常处理

📖 章节简介

本章将介绍Java的异常处理机制,包括try-catch语句、自定义异常和异常链。

Java异常处理流程图

上图概括了 try/catch/finally 与 throw/throws 的关系,便于建立异常处理闭环。

⚠️ 异常基础

1. 异常层次结构

Java
// 异常层次结构
/*
Throwable
├── Error(错误)
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   └── VirtualMachineError
└── Exception(异常)
    ├── RuntimeException(运行时异常)
    │   ├── NullPointerException
    │   ├── ArrayIndexOutOfBoundsException
    │   └── IllegalArgumentException
    ├── IOException(IO异常)
    │   ├── FileNotFoundException
    │   └── EOFException
    └── SQLException(SQL异常)
*/

2. 异常类型

Java
import java.io.*;

// 异常类型示例
public class ExceptionTypes {
    public static void main(String[] args) {
        // 受检异常(Checked Exception)
        try {
            FileReader file = new FileReader("nonexistent.txt");
        } catch (FileNotFoundException e) {
            System.out.println("文件未找到: " + e.getMessage());
        }

        // 运行时异常(Unchecked Exception)
        String str = null;
        try {
            int length = str.length();  // NullPointerException
        } catch (NullPointerException e) {
            System.out.println("空指针异常: " + e.getMessage());
        }

        // 错误(Error)
        try {
            int[] array = new int[Integer.MAX_VALUE];
        } catch (OutOfMemoryError e) {
            System.out.println("内存溢出: " + e.getMessage());
        }
    }
}

🛡️ 异常处理

1. try-catch-finally

Java
// try-catch-finally
public class TryCatchFinally {
    public static void main(String[] args) {
        // 基本异常处理
        try {
            int result = divide(10, 0);
            System.out.println("结果: " + result);
        } catch (ArithmeticException e) {
            System.out.println("算术异常: " + e.getMessage());
        } finally {
            System.out.println("finally块总是执行");
        }

        // 多个catch块
        try {
            String str = null;
            System.out.println(str.length());
        } catch (NullPointerException e) {
            System.out.println("空指针异常");
        } catch (IllegalArgumentException e) {
            System.out.println("非法参数异常");
        } catch (Exception e) {
            System.out.println("其他异常: " + e.getMessage());
        }
    }

    public static int divide(int a, int b) {
        return a / b;
    }
}

2. try-with-resources

Java
// try-with-resources(Java 7+)
import java.io.*;

public class TryWithResources {
    public static void main(String[] args) {
        // 传统方式
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader("file.txt"));
            String line = reader.readLine();
            System.out.println(line);
        } catch (IOException e) {
            System.out.println("IO异常: " + e.getMessage());
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    System.out.println("关闭异常: " + e.getMessage());
                }
            }
        }

        // 使用try-with-resources
        try (BufferedReader reader2 = new BufferedReader(new FileReader("file.txt"))) {  // try-with-resources 自动关闭资源
            String line = reader2.readLine();
            System.out.println(line);
        } catch (IOException e) {
            System.out.println("IO异常: " + e.getMessage());
        }
        // 资源自动关闭
    }
}

🎯 自定义异常

1. 创建自定义异常

Java
// 自定义异常
public class InsufficientFundsException extends Exception {
    private double balance;
    private double amount;

    public InsufficientFundsException(double balance, double amount) {
        super("余额不足,当前余额: " + balance + ",需要金额: " + amount);
        this.balance = balance;
        this.amount = amount;
    }

    public double getBalance() {
        return balance;
    }

    public double getAmount() {
        return amount;
    }
}

// 使用自定义异常(与InsufficientFundsException分属不同文件)
class BankAccount {
    private double balance;

    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }

    public void withdraw(double amount) throws InsufficientFundsException {
        if (amount > balance) {
            throw new InsufficientFundsException(balance, amount);
        }
        balance -= amount;
        System.out.println("取款成功,余额: " + balance);
    }

    public static void main(String[] args) {
        BankAccount account = new BankAccount(1000);

        try {
            account.withdraw(500);
            account.withdraw(600);  // 会抛出异常
        } catch (InsufficientFundsException e) {
            System.out.println(e.getMessage());
            System.out.println("当前余额: " + e.getBalance());
            System.out.println("需要金额: " + e.getAmount());
        }
    }
}

📊 异常链

1. 异常链

Java
// 异常链
public class ExceptionChain {
    public static void main(String[] args) {
        try {
            method1();
        } catch (Exception e) {
            System.out.println("捕获异常: " + e.getMessage());
            System.out.println("原因: " + e.getCause().getMessage());

            // 打印异常栈
            e.printStackTrace();
        }
    }

    public static void method1() throws Exception {
        try {
            method2();
        } catch (Exception e) {
            // 包装原始异常
            throw new Exception("method1失败", e);
        }
    }

    public static void method2() throws Exception {
        try {
            int result = 10 / 0;
        } catch (ArithmeticException e) {
            // 包装原始异常
            throw new Exception("method2失败", e);
        }
    }
}

💡 最佳实践

1. 异常处理原则

Java
import java.io.*;

// 异常处理最佳实践
public class ExceptionBestPractices {
    // ✅ 好的做法:捕获具体异常
    public void readFile1() {
        try {
            FileReader reader = new FileReader("file.txt");
        } catch (FileNotFoundException e) {
            System.out.println("文件未找到");
        } catch (IOException e) {
            System.out.println("IO错误");
        }
    }

    // ❌ 不好的做法:捕获所有异常
    public void readFile2() {
        try {
            FileReader reader = new FileReader("file.txt");
        } catch (Exception e) {
            System.out.println("发生异常");  // 太宽泛
        }
    }

    // ✅ 好的做法:使用try-with-resources
    public void readFile3() {
        try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
            String line = reader.readLine();
            System.out.println(line);
        } catch (IOException e) {
            System.out.println("IO错误");
        }
    }

    // ❌ 不好的做法:忽略异常
    public void readFile4() {
        try {
            FileReader reader = new FileReader("file.txt");
        } catch (IOException e) {
            // 什么都不做
        }
    }
}

2. 异常设计

Java
// 异常设计最佳实践
public class ExceptionDesign {
    // ✅ 好的做法:提供有用的错误信息
    public class InvalidEmailException extends Exception {
        public InvalidEmailException(String email) {
            super("无效的邮箱地址: " + email);
        }
    }

    // ❌ 不好的做法:错误信息不明确
    public class BadException extends Exception {
        public BadException() {
            super("出错了");  // 太模糊
        }
    }

    // ✅ 好的做法:继承适当的异常类
    public class UserNotFoundException extends RuntimeException {
        public UserNotFoundException(String username) {
            super("用户不存在: " + username);
        }
    }

    // ☕ 需要根据场景选择:对于业务逻辑异常,通常更适合继承RuntimeException
    public class UserException extends Exception {
        // 如果不需要强制调用方处理,考虑继承RuntimeException
    }
}

📝 练习题

基础题

  1. 什么是异常?有哪些类型?
  2. try-catch-finally如何工作?
  3. 如何创建自定义异常?

进阶题

  1. 使用try-with-resources管理资源。
  2. 实现异常链。
  3. 设计合理的异常层次结构。

实践题

  1. 创建一个文件处理工具。
  2. 实现一个银行账户系统。
  3. 构建一个网络请求工具类。

📚 推荐阅读

🔗 下一章

集合框架 - 学习Java的集合框架。