Menu
CoddyTech

异常处理是什么?

异常处理是程序应对运行时发生的错误的一种方式。可能出错的代码放在一个受保护的代码块(比如 try)中,当错误被抛出时,程序会跳转到处理程序(比如 except 或 catch),而不是直接崩溃。

作者 Kevin Spektor, 联合创始人兼 CTO

更新于 2026年9月24日

一个询问用户年龄的程序,迟早会收到“twenty”而不是 20。转换这样的文本会导致运行时错误:int("twenty") 会抛出 ValueError,如果事先没有应对方案,程序就会停止。异常处理就是这个应对方案。你标出可能出错的代码,再写好出错时要运行的代码。

Next year you will be 35
Not a number: twenty
Next year you will be 20
Done

错误的答案被报告出来,循环继续处理下一个答案。如果没有 try,程序打印一行之后就会输出一段错误回溯(traceback)。

异常处理是如何工作的

  1. try 中的代码正常运行。
  2. 某个操作失败,运行时抛出一个异常:一个带有类型(ValueError)和消息(invalid literal for int() with base 10: 'twenty')的对象。
  3. try 代码块在出错的那一行停下,剩余的行都被跳过,所以对“twenty”不会打印“Next year you will be”。
  4. 运行时把异常的类型与每个 except 子句从上到下逐一比较,执行第一个匹配的子句。
  5. 处理程序执行完后,程序从整个 try 语句之后的第一行继续运行。
  6. 如果没有子句匹配,异常就会离开当前函数,在调用它的函数中继续寻找,再到那个函数的调用方中寻找。这叫作调用栈展开。如果一直找到程序顶层都没有匹配的处理程序,程序就会带着错误回溯结束。

第 6 步意味着处理程序不必写在出错的那个函数里。在下面的例子中,错误发生在两层调用的深处,底部的处理程序照样能捕获它:

parsing 4.50
parsing free
Could not add the prices: could not convert string to float: 'free'

float("free") 在 parse_price 内部失败了。parse_price 和 total 都没有处理程序,所以两者都被中止,“3.20”根本没有被解析。这就是异常处理带给你的主要设计选择:在你知道该怎么应对错误的地方处理它,而这个地方往往离出错的位置很远。

try、except、else 和 finally

Python 的 try 语句还有两个可选部分。else 只在 try 代码块没有发生异常、顺利执行完时运行。finally 在任何情况下都会运行:成功之后、处理完异常之后,甚至函数从 try 内部返回时也会运行。

no error
finally runs either way
2.0
b was zero
finally runs either way
None

清理工作就放在 finally 里:关闭文件、释放锁、关闭数据库连接。在 Python 中,with 语句会自动完成文件的清理工作,处理文件时通常都用它。

抛出自定义异常

异常并不只用于语言自己检测到的错误。当你自己的函数收到无法处理的输入时,它可以抛出异常来拒绝,由调用方决定接下来怎么做。定义一个小小的异常类,就能给错误起一个能说明问题的名字。

70
Refused: cannot take 250, balance is 100

Python 使用 raise。Java、C++ 和 JavaScript 则用 throw 做同样的事。

其他语言中的异常处理

语言关键字值得了解
Pythontry、except、else、finally、raise每个异常都是继承自 BaseException 的对象
Javatry、catch、finally、throw、throwsIOException 这样的受检异常必须被捕获,或用 throws 声明
C++try、catch、throw没有 finally;栈展开时由析构函数负责清理
JavaScripttry、catch、finally、throwasync 代码中的错误用包住 await 的 try 来捕获
C无函数返回错误码,很多函数还会设置 errno
Go普通错误没有专门的关键字函数返回一个 error 值,由调用方检查

同样的解析示例用 Java 写是这样的:

try {
    int age = Integer.parseInt("twenty");
    System.out.println(age + 1);
} catch (NumberFormatException e) {
    System.out.println("Not a number: " + e.getMessage());
} finally {
    System.out.println("done");
}

Java 把异常分为两类。受检异常(比如 IOException)描述的是程序控制范围之外的问题,如果代码既不捕获也不声明它们,编译器就拒绝构建。非受检异常是 RuntimeException 的子类(比如 NullPointerException),通常意味着代码有 bug,编译器不强制你处理它们。

常见错误

  • 捕获所有异常。 Python 中裸写的 except: 连 KeyboardInterrupt 也会捕获,导致 Ctrl+C 失效;except Exception: 则会捕获你从未预料到的 bug。只捕获你知道如何处理的具体类型。
  • 把错误吞掉。 except ValueError: pass 会把问题藏起来。至少要打印或记录出了什么错。
  • try 代码块太大。 只包住可能抛出你要捕获的那种异常的几行代码,以免误把无关的错误也处理掉。
  • 捕获 bug 而不是修复它。 循环多跑了一步导致的 IndexError 是代码本身的错误。处理它只会让错误的代码继续存活。
  • 指望 catch 能阻止 C 或 C++ 程序崩溃。 段错误是操作系统发出的信号,而不是异常,在 Linux 和 macOS 上,C++ 的 catch (...) 根本看不到它。

下一步学什么

异常处理所应对的错误在运行时错误页面中有介绍,它无法捕获的崩溃则在段错误页面中有介绍。想了解完整的语法,可以阅读 Python 异常、Java try 和 catch、C++ 异常以及 JavaScript try 和 catch 的指南。Java 课程和 Python 课程都会通过真实的练习带你掌握它们。

常见问题

Java 异常处理的 5 个关键字是什么?
try、catch、finally、throw 和 throws。try 标记需要监视的代码,catch 处理特定类型的异常,finally 无论是否发生异常都会执行清理代码。throw 抛出一个异常,方法签名中的 throws 则声明这个方法可能把哪些受检异常交给调用方。
C++ 中的异常处理是什么?
C++ 使用 try、catch 和 throw。任何类型都可以被抛出,但按照惯例,你应该抛出派生自 std::exception 的对象(比如 std::runtime_error),并按引用捕获:catch (const std::exception& e)。C++ 没有 finally;清理工作在析构函数中完成,析构函数会在栈展开时自动运行。
错误和异常有什么区别?
在日常用法中,错误是指任何出了问题的情况,异常则是编程语言用来报告错误的对象。Java 划分得更严格:Exception 涵盖程序可以合理处理的问题,而 Error(比如 OutOfMemoryError 或 StackOverflowError)表示 JVM 本身出了故障,程序通常不应该尝试捕获它。在 Python 中,ValueError 和 ZeroDivisionError 虽然名字里带 Error,但都是异常。
Java 中 throw 和 throws 有什么区别?
throw 是一条语句,在当前位置抛出一个异常:throw new IllegalArgumentException("negative age");。throws 是方法声明的一部分,列出这个方法可能交给调用方的受检异常:void load() throws IOException。之后编译器会要求每个调用方要么捕获这个异常,要么同样声明它。
异常处理会让代码变慢吗?
在大多数现代语言中,进入 try 代码块的开销很小。从 Python 3.11 开始,没有异常抛出时 try 几乎不产生开销;主流 C++ 编译器也不会给没有抛出异常的执行路径增加任何运行时开销。但抛出和捕获异常比一次 if 判断慢得多,所以在对性能要求很高的循环中,应该先检查常见情况,而不是依赖异常。
Coddy 编程语言插图

和 Coddy 一起学编程

开始