Java 异常处理:try-catch-finally 与 throws
Java 程序在运行时可能遇到各种意外情况,比如文件找不到、网络中断或除以零。这些意外被称为“异常”。处理异常的核心目标是不让程序突然崩溃,而是优雅地应对错误并继续执行或安全退出。Java 提供了两套互补的机制:try-catch-finally 用于在当前方法内捕获并处理异常,而 throws 用于将异常传递给调用者处理。
使用 try-catch-finally 捕获并处理异常
当你知道某段代码可能出错,并且你有能力当场解决(比如提示用户重试、使用默认值等),就用 try-catch-finally 结构。
-
把可能出错的代码放进
try块。try { // 可能抛出异常的代码 int result = 10 / 0; // 这里会抛出 ArithmeticException } -
紧接一个或多个
catch块来处理特定类型的异常。
每个catch声明它要捕获的异常类型,并提供处理逻辑:catch (ArithmeticException e) { System.out.println("不能除以零!"); }- 按具体到通用顺序排列
catch块。例如先捕获FileNotFoundException,再捕获更通用的IOException。 - 不要捕获无法处理的异常后留空。如果不知道如何处理,至少打印错误信息:
catch (Exception e) { e.printStackTrace(); // 输出异常堆栈 }
- 按具体到通用顺序排列
-
可选地添加
finally块执行清理操作。
无论是否发生异常,finally中的代码总会执行,适合关闭文件、释放连接等:finally { if (file != null) { file.close(); // 确保文件被关闭 } }
完整示例:
import java.io.*;
public class FileReaderExample {
public static void main(String[] args) {
FileInputStream file = null;
try {
file = new FileInputStream("missing.txt");
int data = file.read();
System.out.println((char) data);
} catch (FileNotFoundException e) {
System.out.println("文件没找到,请检查路径");
} catch (IOException e) {
System.out.println("读取文件时出错");
} finally {
if (file != null) {
try {
file.close();
} catch (IOException e) {
System.out.println("关闭文件失败");
}
}
}
}
}
使用 throws 声明并传递异常
当你编写的方法内部调用了可能抛出异常的代码,但你不想在此处处理,而是希望由调用你的方法来负责,就用 throws。
-
在方法签名末尾添加
throws关键字,后跟异常类型。public void readFile(String path) throws IOException { FileInputStream file = new FileInputStream(path); // 可能抛出 IOException // ... 其他操作 } -
调用该方法的代码必须处理这个异常。
调用者有两个选择:- 用
try-catch捕获:try { obj.readFile("data.txt"); } catch (IOException e) { System.out.println("读取失败"); } - 继续用
throws向上抛出(通常用于主方法或框架入口):public static void main(String[] args) throws IOException { obj.readFile("data.txt"); }
- 用
关键规则:
throws只能用于检查型异常(Checked Exception),如IOException、SQLException。对于非检查型异常(如NullPointerException),编译器不要求声明,但也可以显式声明。- 子类重写父类方法时,不能抛出比父类更宽泛的检查型异常。例如父类方法声明
throws IOException,子类可以抛出FileNotFoundException(它是IOException的子类),但不能抛出Exception。
try-catch-finally 与 throws 的协作关系
两者不是互斥的,而是协同工作。典型模式是:底层方法用 throws 声明异常,高层调用者用 try-catch 处理。
| 场景 | 推荐做法 |
|---|---|
| 你明确知道如何修复错误(如重试、提供默认值) | 用 try-catch 在本地处理 |
| 错误需要用户介入(如输入无效文件名) | 用 try-catch 捕获后提示用户 |
| 方法是工具函数,无法决定如何处理错误 | 用 throws 把异常交给调用者 |
| 需要确保资源释放(如关闭数据库连接) | 在 finally 块中执行清理,或使用 try-with-resources |
注意:从 Java 7 开始,推荐用 try-with-resources 替代手动 finally 关闭资源。只要资源类实现了 AutoCloseable 接口,就能自动关闭:
try (FileInputStream file = new FileInputStream("data.txt")) {
int data = file.read();
System.out.println((char) data);
} catch (IOException e) {
System.out.println("操作失败");
}
// file 会自动关闭,无需 finally
异常处理的常见错误与最佳实践
-
避免空的
catch块。
记录异常信息,即使只是打印堆栈:catch (Exception e) { // 至少保留这行 e.printStackTrace(); } -
不要用异常控制正常流程。
例如,不要用catch来判断文件是否存在;应先用File.exists()检查。 -
自定义异常要继承合适的父类。
- 如果希望调用者必须处理,继承
Exception(检查型)。 - 如果属于编程错误(如参数非法),继承
RuntimeException(非检查型)。
- 如果希望调用者必须处理,继承
-
在
finally中避免抛出新异常。
如果finally里的代码可能出错,用嵌套try-catch包裹它,否则会掩盖原始异常。 -
早抛出,晚捕获(Fail-fast)。
在问题源头尽早抛出异常(如方法开头校验参数),在高层逻辑(如 UI 层)统一捕获处理。

暂无评论,快来抢沙发吧!