第六章:Java中的异常
6.1 异常概述
在Java编程中,异常(Exception)是指在程序运行过程中发生的非正常情况,它会中断正常的指令流。Java通过异常处理机制,使程序能够优雅地处理运行时错误,提高程序的健壮性和可靠性。
6.2 异常体系结构
Java的异常类都继承自java.lang.Throwable类,主要分为两大类:
- Error:系统级错误,通常由Java虚拟机(JVM)抛出,如
OutOfMemoryError、StackOverflowError等。这类错误一般无法通过程序处理。 - Exception:程序级异常,可分为:
- Checked Exception(受检异常):编译时检查的异常,必须被捕获或声明抛出,如
IOException、SQLException。 - Unchecked Exception(非受检异常):运行时异常,编译时不强制检查,如
NullPointerException、ArrayIndexOutOfBoundsException。
- Checked Exception(受检异常):编译时检查的异常,必须被捕获或声明抛出,如
6.3 异常处理关键字
Java提供了五个关键字来处理异常:
- try:用于包裹可能抛出异常的代码块。
- catch:用于捕获并处理特定类型的异常。
- finally:无论是否发生异常都会执行的代码块,常用于资源清理。
- throw:在方法内部手动抛出一个异常对象。
- throws:在方法声明中指明该方法可能抛出的异常类型。
6.4 try-catch-finally 基本用法
public class ExceptionDemo { public static void main(String[] args) { try { // 可能抛出异常的代码 int result = 10 / 0; // 这里会抛出 ArithmeticException System.out.println("结果:" + result); } catch (ArithmeticException e) { // 捕获并处理算术异常 System.out.println("发生算术异常:" + e.getMessage()); } finally { // 无论是否发生异常都会执行 System.out.println("finally 块执行完成"); } } }6.5 多重catch块
一个try块可以跟随多个catch块,用于捕获不同类型的异常。需要注意的是,catch块的顺序应该从具体到一般(子类到父类)。
try { // 可能抛出多种异常的代码 FileInputStream fis = new FileInputStream("test.txt"); int data = fis.read(); fis.close(); } catch (FileNotFoundException e) { System.out.println("文件未找到:" + e.getMessage()); } catch (IOException e) { System.out.println("IO异常:" + e.getMessage()); } catch (Exception e) { System.out.println("其他异常:" + e.getMessage()); }6.6 try-with-resources(Java 7+)
Java 7引入了try-with-resources语句,可以自动关闭实现了AutoCloseable接口的资源,简化了资源管理代码。
try (FileInputStream fis = new FileInputStream("test.txt"); BufferedReader br = new BufferedReader(new InputStreamReader(fis))) { String line; while ((line = br.readLine()) != null) { System.out.println(line); } } catch (IOException e) { System.out.println("读取文件时发生异常:" + e.getMessage()); }6.7 自定义异常
除了使用Java内置的异常类,我们还可以创建自定义异常类来满足特定的业务需求。
// 自定义受检异常 public class InsufficientBalanceException extends Exception { public InsufficientBalanceException(String message) { super(message); } } // 自定义运行时异常 public class InvalidAgeException extends RuntimeException { public InvalidAgeException(String message) { super(message); } } // 使用示例 public class BankAccount { private double balance; public void withdraw(double amount) throws InsufficientBalanceException { if (amount > balance) { throw new InsufficientBalanceException("余额不足,当前余额:" + balance); } balance -= amount; } }6.8 异常处理的最佳实践
- 不要捕获所有异常:避免使用
catch (Exception e)捕获所有异常,应该只捕获你能处理的异常。 - 不要忽略异常:空的catch块会隐藏错误,至少应该记录异常信息。
- 使用具体的异常类型:抛出和捕获尽可能具体的异常类型。
- finally块中不要抛出异常:finally块中的异常会覆盖try或catch块中的异常。
- 异常信息要明确:抛出的异常应该包含足够的信息来诊断问题。
- 考虑使用异常链:通过
initCause()或构造函数保留原始异常信息。
6.9 常见异常类型及原因
| 异常类型 | 常见原因 | 示例 |
|---|---|---|
NullPointerException | 尝试访问null对象的属性或方法 | String str = null; str.length(); |
ArrayIndexOutOfBoundsException | 数组索引越界 | int[] arr = new int[3]; arr[5] = 10; |
ClassCastException | 类型转换错误 | Object obj = "hello"; Integer num = (Integer) obj; |
NumberFormatException | 字符串转换为数字格式错误 | Integer.parseInt("abc"); |
IllegalArgumentException | 方法参数不合法 | Thread.sleep(-100); |
6.10 总结
异常处理是Java编程中的重要组成部分。合理的异常处理能够:
- 提高程序的健壮性和可靠性
- 提供清晰的错误信息和调试线索
- 确保资源的正确释放
- 分离正常逻辑和错误处理逻辑
在实际开发中,应该根据具体场景选择合适的异常处理策略,遵循最佳实践,编写出既健壮又易于维护的代码。