核心定义
在 Java 中,异常 是指程序在运行过程中出现的非正常情况或错误,它是一个事件,它会中断程序正常的指令执行流程。

异常就是程序运行时发生的一些“意外”或“问题”。
- 你想让程序读取一个文件,但文件不存在。
- 你想让程序进行除法运算,但除数是零。
- 你想让程序访问一个数组的第 10 个元素,但数组长度只有 5。
如果没有异常处理机制,这些错误就会导致程序直接崩溃(JVM 会打印出错误堆栈信息并终止程序)。
异常的层次结构
Java 中的所有异常类都继承自 java.lang.Throwable 类。Throwable 有两个重要的子类:
-
Error(错误):
(图片来源网络,侵删)- 定义:
Error类及其子类通常表示严重的、系统级别的问题,这些问题通常是不可恢复的。 - 特点:
- 由 JVM(Java 虚拟机)或底层硬件引发。
- 编程人员通常无法也不应该捕获和处理它们。
OutOfMemoryError(内存溢出)、StackOverflowError(栈溢出)、VirtualMachineError(虚拟机错误)。
- 处理方式: 遇到
Error,程序基本只能终止,程序员需要从代码优化、系统配置等层面去解决,而不是在代码里用try-catch捕获。
- 定义:
-
Exception(异常):- 定义:
Exception类及其子类表示程序本身可以处理的异常情况,这是我们日常编码中主要处理的对象。 - 特点:
- 可以被
try-catch语句捕获并处理。 - 可以被
throws关键字声明,由上一级调用者处理。
- 可以被
Exception又可以分为两大类:
a. 受检异常:
-
定义: 也叫“已检查异常”,在编译时,Java 编译器会检查是否对这类异常进行了处理(要么用
try-catch捕获,要么用throws声明抛出)。 -
目的: 强制程序员处理那些预期可能发生且可恢复的异常,提高程序的健壮性。
(图片来源网络,侵删) -
特点: 都是
Exception类的子类,但不是RuntimeException的子类。 -
常见例子:
IOException: 文件操作时可能发生的异常。SQLException: 数据库操作时可能发生的异常。FileNotFoundException: 文件未找到异常。
-
示例代码:
import java.io.FileInputStream; import java.io.FileNotFoundException; public class CheckedExceptionExample { public static void main(String[] args) { // FileNotException 是受检异常,必须处理 try { FileInputStream fis = new FileInputStream("non_existent_file.txt"); } catch (FileNotFoundException e) { System.out.println("文件未找到,请检查路径!"); e.printStackTrace(); // 打印异常堆栈信息 } System.out.println("程序继续运行..."); } }
b. 非受检异常:
- 定义: 也叫“未检查异常”,编译器不会检查这类异常,它们通常是程序中的逻辑错误或非法操作,本应在编码时避免。
- 目的: 提醒程序员修复代码中的潜在问题。
- 特点: 都是
RuntimeException类或其子类的实例。 - 常见例子:
NullPointerException: 空指针异常,试图在一个null对象上调用方法或访问其属性。ArrayIndexOutOfBoundsException: 数组下标越界异常,访问了不存在的数组索引。ArithmeticException: 算术异常,整数除以零。ClassCastException: 类型转换异常,将一个对象强制转换为它不是实例的子类。
- 示例代码:
public class UncheckedExceptionExample { public static void main(String[] args) { // ArithmeticException 是非受检异常,编译器不强制处理 int a = 10; int b = 0; // 这行代码会抛出 ArithmeticException int result = a / b; // 编译时不会报错,但运行时会崩溃 System.out.println(result); } }
- 定义:
异常处理机制
Java 提供了 try-catch-finally 和 throws 关键字来处理异常。
try-catch-finally 语句
这是最核心的异常处理方式。
try块: 将可能抛出异常的代码块放在try里面。catch块: 捕获try块中发生的特定异常,并进行处理,可以有多个catch块来捕获不同类型的异常。finally块: 无论是否发生异常,finally块中的代码一定会被执行,通常用于释放资源,如关闭文件流、数据库连接等。
语法格式:
try {
// 可能发生异常的代码
} catch (ExceptionType1 e1) {
// 处理 ExceptionType1 类型的异常
} catch (ExceptionType2 e2) {
// 处理 ExceptionType2 类型的异常
} finally {
// 无论是否发生异常,都会执行的代码(通常用于资源清理)
}
示例:
public class TryCatchExample {
public static void main(String[] args) {
int[] numbers = {1, 2, 3};
try {
System.out.println("访问数组元素 4: " + numbers[4]); // 会抛出 ArrayIndexOutOfBoundsException
System.out.println("这行代码不会执行");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("捕获到异常: 数组索引越界!");
// e.printStackTrace(); // 打印详细的错误堆栈
} finally {
System.out.println("finally 块被执行,用于清理资源。");
}
System.out.println("程序继续运行...");
}
}
throws 关键字
当一个方法内部处理不了某个异常时,可以使用 throws 关键字声明该方法可能会抛出的异常,将异常的处理责任交给方法的调用者。
语法格式:
returnType methodName(paramList) throws ExceptionType1, ExceptionType2 {
// 方法体,可能抛出 ExceptionType1 或 ExceptionType2
}
示例:
import java.io.IOException;
public class ThrowsExample {
// readFile 方法声明自己可能会抛出 IOException
public static void readFile() throws IOException {
// 模拟文件读取失败
throw new IOException("文件读取失败");
}
public static void main(String[] args) {
try {
readFile(); // 调用者必须处理 readFile 抛出的异常
} catch (IOException e) {
System.out.println("在 main 方法中捕获到异常: " + e.getMessage());
}
System.out.println("程序继续运行...");
}
}
最佳实践
-
具体化异常: 尽量捕获具体的异常类型,而不是笼统地捕获
Exception,这样可以更精确地处理问题。// 不推荐 } catch (Exception e) { // 推荐 } catch (IOException e) { } catch (SQLException e) { -
不要吃掉异常: 只捕获异常但不做任何处理(如只打印一个空日志)或
catch (Exception e) { }是非常危险的,这会隐藏问题,导致程序在错误状态下继续运行,引发更难排查的 Bug。 -
使用
finally或try-with-resources释放资源: 对于文件流、数据库连接等资源,一定要确保它们被关闭。finally块可以保证这一点,但从 Java 7 开始,推荐使用try-with-resources语句,它能更优雅、更安全地自动关闭实现了AutoCloseable接口的资源。// try-with-resources (推荐) try (FileInputStream fis = new FileInputStream("file.txt")) { // 使用 fis } catch (IOException e) { e.printStackTrace(); } // fis 会在 try 块结束后自动关闭 -
不要用异常来控制流程: 异常是用于处理“异常”情况的,不应该被用作常规的流程控制(用异常来处理
if-else能判断的逻辑),这会影响性能。
| 特性 | 描述 |
|---|---|
| 定义 | 程序运行时发生的非正常事件,中断正常流程。 |
| 根类 | java.lang.Throwable |
| 两大分支 | Error (系统级严重错误,不应捕获) 和 Exception (可处理的异常) |
| Exception 分类 | 受检异常 (编译器检查,必须处理) 和 非受检异常 (运行时错误,通常是代码逻辑问题) |
| 处理方式 | try-catch-finally (捕获并处理) 和 throws (声明抛出,由调用者处理) |
| 核心目的 | 提高程序的健壮性和容错性,使程序在出现问题时不会轻易崩溃,并能优雅地处理错误。 |
