Java 异常类
最后修改时间:2025 年 4 月 13 日
java.lang.Exception 类是 Java 中所有异常的超类。 异常是运行时事件,会中断正常的执行流程并发出信号,表明需要处理这些问题以实现健壮的应用程序行为。 正确的异常管理可确保错误恢复,增强稳定性和改进调试。
Java 异常分为两个主要类别:检查型异常(编译时)和 非检查型异常(运行时)。 检查型异常必须显式处理,可以通过捕获它们或在方法签名中声明它们,而非检查型异常是由于编程错误而发生的,并且可以在没有强制处理的情况下传播。 了解异常类型和处理机制对于编写可靠、可维护的代码至关重要。
异常类层次结构
在 Java 的异常层次结构中,Exception 类直接位于 Throwable 之下。 它有两个关键子类:RuntimeException(代表非检查型异常)和 IOException(一种常见的检查型异常类型)。 这种区别决定了异常在代码中的管理方式。
java.lang.Throwable
├── java.lang.Error
└── java.lang.Exception
├── java.lang.RuntimeException
│ ├── java.lang.NullPointerException
│ ├── java.lang.IllegalArgumentException
│ ├── java.lang.IndexOutOfBoundsException
│ └── ...
├── java.lang.IOException
│ ├── java.lang.FileNotFoundException
│ ├── java.lang.UnsupportedEncodingException
│ └── ...
├── java.lang.ReflectiveOperationException
├── java.lang.InterruptedException
└── Other checked exceptions
主要区别:检查型与非检查型异常
- 检查型异常: 这些异常必须使用
try-catch进行处理,或者使用throws声明。 示例:IOException、SQLException、InterruptedException。 - 非检查型异常: 这些异常扩展了
RuntimeException,通常是由于编程逻辑错误造成的,例如NullPointerException和IllegalArgumentException。 处理它们是可选的,但建议这样做。 - 错误 (Errors): 与异常不同,错误表示严重问题(例如,
OutOfMemoryError),并且通常是不可恢复的。
检查型异常和非检查型异常之间的区别有助于开发人员预测故障点,改进程序流程并确保正确处理意外情况。
基本异常处理
Java 提供了 try-catch 块来处理异常。 try 块包含可能抛出异常的代码,而 catch 块定义了发生异常时如何处理该异常。 这可以防止程序突然终止。
在实践中,许多异常(例如除以零)都是通过在执行操作之前使用条件检查来避免的,而不是依赖于事后捕获它们。 积极主动的方法可以提高程序的稳定性并避免不必要的异常处理。
package com.zetcode;
public class Main {
public static void main(String[] args) {
try {
int result = divide(10, 0);
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Error: " + e.getMessage());
e.printStackTrace();
}
}
public static int divide(int a, int b) {
return a / b;
}
}
此示例演示了基本的异常处理。 divide 方法在除以零时抛出 ArithmeticException。 但是,在实际应用中,在执行除法之前检查零将是首选方法,以防止异常发生。
检查型与非检查型异常
检查型异常必须声明或处理,而非检查型异常则不需要。 检查型异常扩展了 Exception 但没有扩展 RuntimeException。 非检查型异常扩展了 RuntimeException 或 Error。
package com.zetcode;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// Unchecked exception (no need to declare)
try {
String str = null;
System.out.println(str.length());
} catch (NullPointerException e) {
System.out.println("NullPointerException caught");
}
// Checked exception (must be handled)
try {
readFile("nonexistent.txt");
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getMessage());
}
}
public static void readFile(String path) throws FileNotFoundException {
File file = new File(path);
Scanner scanner = new Scanner(file);
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
scanner.close();
}
}
此示例对比了检查型异常和非检查型异常。 NullPointerException 是非检查型的,不需要声明。 FileNotFoundException 是检查型的,必须在方法签名中使用 throws 捕获或声明。
创建自定义异常
可以通过扩展 Exception 或 RuntimeException 来创建自定义异常。 它们应该提供与超类构造函数匹配的构造函数。 自定义异常对于特定于应用程序的错误情况很有用。
package com.zetcode;
class InsufficientFundsException extends Exception {
private double amount;
public InsufficientFundsException(double amount) {
super("Insufficient funds: " + amount);
this.amount = amount;
}
public double getAmount() {
return amount;
}
}
class BankAccount {
private double balance;
public BankAccount(double balance) {
this.balance = balance;
}
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException(amount - balance);
}
balance -= amount;
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount(500);
try {
account.withdraw(600);
} catch (InsufficientFundsException e) {
System.out.println(e.getMessage());
System.out.println("Missing amount: " + e.getAmount());
}
}
}
此示例显示了一个自定义的 InsufficientFundsException。 该异常包含有关缺失金额的附加信息。 当提款超过余额时,BankAccount 类会抛出此异常。 main 方法捕获并处理自定义异常。
多个 Catch 块
多个 catch 块可以处理不同的异常类型。 更具体的异常应该放在更一般的异常之前。 Java 7 引入了 multi-catch,用于在一个块中处理多个异常。
package com.zetcode;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("Enter numerator: ");
int numerator = scanner.nextInt();
System.out.print("Enter denominator: ");
int denominator = scanner.nextInt();
int result = numerator / denominator;
System.out.println("Result: " + result);
} catch (InputMismatchException e) {
System.out.println("Invalid input - must be integer");
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero");
} catch (Exception e) {
System.out.println("An unexpected error occurred");
} finally {
scanner.close();
System.out.println("Scanner closed in finally block");
}
}
}
此示例演示了多个 catch 块。 InputMismatchException 处理非整数输入。 ArithmeticException 处理除以零。 更通用的 Exception 捕获任何其他错误。 finally 块确保扫描器始终关闭,无论是否发生异常。
Try-With-Resources
Try-with-resources 会自动关闭实现 AutoCloseable 的资源。 这消除了对显式 finally 块进行资源清理的需求。 它在 Java 7 中引入,以简化资源管理。
package com.zetcode;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
String path = "example.txt";
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
}
}
}
此示例使用 try-with-resources 读取文件。 BufferedReader 在 try 语句中声明,并在块退出时自动关闭。 这种方法比使用 finally 块进行手动资源管理更简洁。 如果文件读取失败,则捕获 IOException。
异常传播
异常向上调用堆栈传播,直到被捕获为止。 方法可以声明它们抛出但不处理的异常。 这允许在应用程序中的适当级别集中进行异常处理。
package com.zetcode;
class DataProcessor {
public void processData(String data) throws IllegalArgumentException {
if (data == null || data.isEmpty()) {
throw new IllegalArgumentException("Invalid data");
}
System.out.println("Processing: " + data);
}
}
class DataService {
private DataProcessor processor = new DataProcessor();
public void handleData(String data) {
try {
processor.processData(data);
} catch (IllegalArgumentException e) {
System.out.println("Service error: " + e.getMessage());
// Could log error or perform recovery here
}
}
}
public class Main {
public static void main(String[] args) {
DataService service = new DataService();
service.handleData("Valid data");
service.handleData(""); // Will trigger exception
}
}
此示例显示了异常传播。 DataProcessor 针对无效数据抛出 IllegalArgumentException。 DataService 捕获并处理此异常。 main 方法不需要处理该异常,因为它已在服务级别处理。 这演示了分层异常处理。
来源
在本文中,我们介绍了 Java Exception 类以及带有实际示例的异常处理。 正确的异常处理对于构建健壮且可维护的 Java 应用程序至关重要。
作者
列出所有Java教程。