【HeadFirst系列之HeadFirstJava】第11天之Java 异常处理:如何优雅地应对有风险的行为?

Scroll Down

Java 异常处理:如何优雅地应对有风险的行为?

在 Java 开发中,异常(Exception) 是我们必须面对的现实。程序运行时,可能会遇到各种不可预见的错误,比如文件未找到、网络连接失败、数组越界等。如果不妥善处理,程序可能会崩溃,影响用户体验,甚至导致数据丢失。

本篇文章基于《Head First Java》第 11 章 “异常处理——有风险的行为”,深入探讨 Java 的异常机制,并结合 代码示例,让你掌握如何编写健壮、可维护的 Java 代码。

11-异常处理-有风险的行为-前言

1. 为什么需要异常处理?

想象这样一个场景:你写了一个 Java 程序,用来读取文件中的数据并进行处理。如果文件不存在,程序应该怎么办?

  • 方案 1:不做异常处理

    • 直接尝试读取文件,但如果文件不存在,程序会直接崩溃。
  • 方案 2:手动检查

    • 在代码中使用 File.exists() 先检查文件是否存在,再决定是否读取。
  • 方案 3:使用 Java 异常处理机制

    • 让 Java 负责检测异常,并提供合理的处理方案。

Java 选择了方案 3,它提供了一套强大的 异常处理机制,让程序能够优雅地应对 不可预见的错误,避免程序崩溃,提高代码的可读性和健壮性。


2. Java 中的异常分类

Java 的异常分为 两大类

  1. 已检查异常(Checked Exception)

    • 必须在编译期处理,否则代码无法通过编译。
    • 例如IOExceptionSQLException
    • 典型场景:文件操作、数据库访问、网络连接等。
  2. 未检查异常(Unchecked Exception)

    • 继承自 RuntimeException,编译器不会强制要求处理。
    • 例如NullPointerExceptionArrayIndexOutOfBoundsException
    • 典型场景:除数为 0、访问空对象、数组越界等。

示例代码

// 已检查异常(编译期必须处理)
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class CheckedExceptionExample {
    public static void main(String[] args) {
        try {
            Scanner scanner = new Scanner(new File("non_existent_file.txt")); // 可能抛出 FileNotFoundException
        } catch (FileNotFoundException e) {
            System.out.println("文件未找到,请检查文件路径!");
        }
    }
}
// 未检查异常(运行时报错)
public class UncheckedExceptionExample {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3};
        System.out.println(numbers[3]); // 运行时报错:ArrayIndexOutOfBoundsException
    }
}

3. 使用 try-catch 处理异常

在 Java 中,try-catch 语句用于捕获和处理异常,保证程序不会因异常崩溃。

示例:捕获除零异常

public class TryCatchExample {
    public static void main(String[] args) {
        try {
            int result = 10 / 0; // 可能抛出 ArithmeticException
        } catch (ArithmeticException e) {
            System.out.println("错误:除数不能为 0!");
        }
        System.out.println("程序继续运行...");
    }
}

catch 可以捕获多个异常

public class MultiCatchExample {
    public static void main(String[] args) {
        try {
            int[] arr = new int[3];
            arr[5] = 10; // 数组越界异常
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组越界异常:" + e.getMessage());
        } catch (Exception e) {
            System.out.println("发生未知错误:" + e);
        }
    }
}

catch 中的异常顺序

多个 catch 语句时,顺序很重要! 需要先捕获子类异常,再捕获父类异常,否则编译错误:

try {
    int a = 5 / 0;
} catch (Exception e) {
    System.out.println("捕获所有异常");
} 
// catch (ArithmeticException e) {  // ❌ 这行会报错,因为 Exception 已经捕获了所有异常
//     System.out.println("数学计算异常");
// }

4. finally 代码块

finally 代码块无论是否发生异常,都会执行,通常用于释放资源(如文件、数据库连接)。

示例:finally 释放资源

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class FinallyExample {
    public static void main(String[] args) {
        Scanner scanner = null;
        try {
            scanner = new Scanner(new File("test.txt"));
            System.out.println("文件读取成功");
        } catch (FileNotFoundException e) {
            System.out.println("文件未找到");
        } finally {
            if (scanner != null) {
                scanner.close(); // 释放资源
                System.out.println("Scanner 关闭");
            }
        }
    }
}

5. 抛出异常:throwthrows

throw 用于手动抛出异常

public class ThrowExample {
    public static void main(String[] args) {
        int age = -5;
        if (age < 0) {
            throw new IllegalArgumentException("年龄不能是负数");
        }
    }
}

throws 声明方法可能抛出的异常

import java.io.IOException;

public class ThrowsExample {
    public static void riskyMethod() throws IOException {
        throw new IOException("IO 发生错误");
    }

    public static void main(String[] args) {
        try {
            riskyMethod();
        } catch (IOException e) {
            System.out.println("捕获异常:" + e.getMessage());
        }
    }
}

6. 自定义异常

在 Java 中,你可以创建自己的异常类,继承 ExceptionRuntimeException

// 自定义异常类
class MyCustomException extends Exception {
    public MyCustomException(String message) {
        super(message);
    }
}

// 使用自定义异常
public class CustomExceptionExample {
    public static void validateAge(int age) throws MyCustomException {
        if (age < 18) {
            throw new MyCustomException("未成年人不允许注册!");
        }
    }

    public static void main(String[] args) {
        try {
            validateAge(16);
        } catch (MyCustomException e) {
            System.out.println("捕获异常:" + e.getMessage());
        }
    }
}

7. 结论

异常处理是 Java 保障程序健壮性的关键机制
使用 try-catch-finally 捕获异常,确保程序不会崩溃
throw 手动抛出异常,throws 声明可能发生的异常
合理使用自定义异常,提高代码可读性


💡 你是否在项目中遇到过异常处理的坑?欢迎留言讨论! 🚀