在 Ruby 中,异常(Exception)是一种用于处理运行时错误的机制。当程序遇到无法正常执行的情况时,会抛出异常(raise an exception),如果没有被捕获(rescued),程序将终止并显示错误信息。
本文将详细介绍 Ruby 的异常机制,包括异常类层级、begin-rescue
结构、自定义异常、确保执行的 ensure
以及异常传播方式等。
📌 目录
- 🔹 Ruby 异常概述
- 🔹 Ruby 异常类层级
- 🔹 捕获异常 (
begin-rescue
) - 🔹
ensure
代码块(无论异常是否发生都执行) - 🔹
else
代码块(仅在没有异常时执行) - 🔹
raise
关键字(抛出异常) - 🔹 自定义异常
- 🔹
retry
关键字(重新执行代码) - 🔹
throw
和catch
(高级异常处理) - 🔹 参考资料
🔹 Ruby 异常概述
在 Ruby 中,所有异常都是 Exception
类的子类。当发生错误时,Ruby 会创建一个异常对象并沿着调用栈传播,直到被捕获或终止程序。
示例:没有异常处理的代码
puts 10 / 0 # ZeroDivisionError: divided by 0
上面的代码会引发 ZeroDivisionError
并导致程序崩溃。因此,我们需要异常处理来防止程序意外终止。
🔹 Ruby 异常类层级
Ruby 提供了许多异常类,每种异常类型代表不同的错误情况。
部分常见的异常类层级如下(继承自 Exception
):
Exception
├── NoMemoryError
├── ScriptError
│ ├── LoadError
│ ├── NotImplementedError
│ ├── SyntaxError
├── SignalException
│ ├── Interrupt
├── StandardError(大多数异常继承自它)
│ ├── ArgumentError
│ ├── IOError
│ ├── IndexError
│ │ ├── KeyError
│ ├── NameError
│ │ ├── NoMethodError
│ ├── RuntimeError
│ ├── SystemCallError
│ ├── TypeError
│ ├── ZeroDivisionError
├── SystemExit
├── Fatal
注意:Ruby 的大多数常见异常都继承自
StandardError
。
🔹 捕获异常 (begin-rescue
)
在 Ruby 中,我们使用 begin-rescue
结构来捕获异常,以避免程序崩溃。
基本使用
begin
puts 10 / 0
rescue ZeroDivisionError
puts "错误: 除数不能为零!"
end
输出:
错误: 除数不能为零!
当 10 / 0
发生 ZeroDivisionError
时,rescue
代码块会捕获异常并执行相应代码。
捕获多个异常
可以在 rescue
中捕获多个异常类型,使用 rescue A, B
形式:
begin
File.open("不存在的文件.txt")
rescue ZeroDivisionError, Errno::ENOENT => e
puts "发生错误: #{e.class} - #{e.message}"
end
输出:
发生错误: Errno::ENOENT - No such file or directory
🔹 ensure
代码块(无论异常是否发生都执行)
无论是否发生异常,ensure
代码块都会执行,适用于释放资源(如关闭文件、断开数据库连接等)。
begin
puts 10 / 0
rescue ZeroDivisionError
puts "发生错误: 除数不能为零!"
ensure
puts "这个代码块无论如何都会执行"
end
输出:
发生错误: 除数不能为零!
这个代码块无论如何都会执行
🔹 else
代码块(仅在没有异常时执行)
else
代码块用于当 begin
代码块 没有发生异常时 执行:
begin
puts "正常执行的代码"
rescue
puts "捕获到异常"
else
puts "没有异常,执行此代码"
end
输出:
正常执行的代码
没有异常,执行此代码
🔹 raise
关键字(抛出异常)
可以使用 raise
主动抛出异常:
def divide(a, b)
raise ArgumentError, "除数不能为零" if b == 0
a / b
end
begin
puts divide(10, 0)
rescue ArgumentError => e
puts "捕获到异常: #{e.message}"
end
输出:
捕获到异常: 除数不能为零
🔹 自定义异常
可以继承 StandardError
来创建自定义异常:
class MyCustomError < StandardError; end
def test_error
raise MyCustomError, "这是一个自定义异常"
end
begin
test_error
rescue MyCustomError => e
puts "捕获到自定义异常: #{e.message}"
end
输出:
捕获到自定义异常: 这是一个自定义异常
🔹 retry
关键字(重新执行代码)
retry
用于重新执行 begin
代码块,但需谨慎使用,否则可能导致无限循环。
attempt = 0
begin
attempt += 1
puts "尝试执行: 第 #{attempt} 次"
raise "故意抛出异常" if attempt < 3
rescue
retry if attempt < 3
end
输出:
尝试执行: 第 1 次
尝试执行: 第 2 次
尝试执行: 第 3 次
🔹 throw
和 catch
(高级异常处理)
与 raise-rescue
不同,catch-throw
适用于非错误情况下的流程控制。
catch(:done) do
(1..5).each do |i|
throw :done if i == 3
puts i
end
end
输出:
1
2
当 i == 3
时,throw :done
直接终止 catch(:done)
代码块的执行。
发表回复