Ruby 异常
在 Ruby 中,异常(Exception) 是处理程序运行时错误(如文件不存在、除以零等)的机制。Ruby 提供了强大的异常处理功能,通过 begin
、rescue
、ensure
和 raise
等关键字实现。以下是对 Ruby 异常的中文讲解,涵盖异常的创建、捕获、处理及常见用法,力求简洁清晰。
1. 什么是异常?
异常是程序运行时发生的错误或意外情况,会中断正常执行流程。Ruby 使用异常对象(继承自 Exception
类)表示错误,常见子类包括 StandardError
(默认捕获的异常基类)、RuntimeError
、ArgumentError
、Errno::ENOENT
(文件不存在)等。
2. 异常处理结构
Ruby 使用 begin
…rescue
…ensure
结构捕获和处理异常。
基本语法
begin
# 可能引发异常的代码
rescue [异常类] => 变量
# 处理异常
else
# 未发生异常时执行
ensure
# 无论是否发生异常都执行
end
示例
begin
result = 10 / 0
rescue ZeroDivisionError => e
puts "错误:#{e.message}" # 输出:错误:divided by 0
ensure
puts "清理工作"
end
# 输出:
# 错误:divided by 0
# 清理工作
说明:
rescue
:捕获指定异常类(如ZeroDivisionError
)。=> e
:将异常对象赋值给变量e
,可访问e.message
(错误信息)或e.backtrace
(调用栈)。else
:无异常时执行(可选)。ensure
:无论是否有异常都执行(可选,常用于清理资源)。
3. 抛出异常
使用 raise
(或 fail
)手动抛出异常。
基本用法
def divide(a, b)
raise ArgumentError, "除数不能为零" if b == 0
a / b
end
begin
divide(10, 0)
rescue ArgumentError => e
puts e.message # 输出:除数不能为零
end
简写
- 不指定异常类,默认抛出
RuntimeError
。
raise "这是一个错误" # 等价于 raise RuntimeError, "这是一个错误"
- 指定异常类:
raise ArgumentError.new("无效参数")
4. 捕获多种异常
可以指定多个异常类,或省略异常类捕获所有 StandardError
子类。
begin
File.open("nonexistent.txt", "r")
rescue Errno::ENOENT
puts "文件不存在"
rescue Errno::EACCES
puts "无权限访问"
rescue StandardError => e
puts "其他错误:#{e.message}"
end
说明:
- 按顺序匹配异常类,优先捕获更具体的异常。
- 不指定异常类时,默认捕获
StandardError
及其子类。
5. 异常类层次结构
Ruby 的异常类继承自 Exception
,常见子类包括:
StandardError
:大多数标准异常的基类(如RuntimeError
,ArgumentError
,ZeroDivisionError
)。Errno::*
:系统相关错误(如Errno::ENOENT
表示文件不存在)。NoMethodError
:调用不存在的方法。TypeError
:类型错误。Exception
:所有异常的根类(不建议直接捕获,以免捕获严重错误如NoMemoryError
)。
推荐:通常只捕获 StandardError
或其子类,避免捕获 Exception
。
6. 自定义异常
可以定义自定义异常类,继承 StandardError
或其他异常类。
class MyCustomError < StandardError
end
def check_positive(num)
raise MyCustomError, "必须是正数" if num <= 0
num
end
begin
check_positive(-5)
rescue MyCustomError => e
puts e.message # 输出:必须是正数
end
7. 重试(retry)
在 rescue
块中使用 retry
可以重试 begin
块的代码。
attempts = 0
begin
attempts += 1
raise "失败" if attempts < 3
puts "成功"
rescue
puts "第 #{attempts} 次尝试失败"
retry if attempts < 3
end
# 输出:
# 第 1 次尝试失败
# 第 2 次尝试失败
# 成功
注意:谨慎使用 retry
,避免无限循环。
8. 异常的传播
如果异常未被捕获,会沿调用栈向上传播,最终终止程序。
def risky_method
raise "出错了"
end
begin
risky_method
rescue => e
puts e.message # 输出:出错了
end
9. 注意事项
- 只捕获必要的异常:避免捕获所有异常(
rescue Exception
),以免隐藏严重问题。
# 不推荐
begin
1 / 0
rescue Exception
puts "捕获所有异常"
end
- 清理资源:使用
ensure
确保文件、连接等资源被正确关闭。
file = nil
begin
file = File.open("example.txt", "r")
# 操作文件
rescue Errno::ENOENT
puts "文件不存在"
ensure
file.close if file
end
- 块方式优先:使用
File.open
等带块的方法,自动管理资源。 - 异常信息:使用
e.message
和e.backtrace
获取详细信息。
begin
raise "错误"
rescue => e
puts e.backtrace.join("\n") # 输出调用栈
end
- 性能:异常处理有开销,避免在正常流程中频繁使用
raise
和rescue
。
10. 示例:综合应用
module FileProcessor
class FileNotFoundError < StandardError; end
def self.process_file(filename)
raise FileNotFoundError, "文件 #{filename} 不存在" unless File.exist?(filename)
File.open(filename, "r") do |file|
file.each_line { |line| puts line }
end
end
end
begin
FileProcessor.process_file("nonexistent.txt")
rescue FileProcessor::FileNotFoundError => e
puts "错误:#{e.message}"
rescue StandardError => e
puts "其他错误:#{e.message}"
ensure
puts "处理完成"
end
# 输出:
# 错误:文件 nonexistent.txt 不存在
# 处理完成
11. 总结
Ruby 的异常处理通过 begin
、rescue
、ensure
和 raise
实现,提供了灵活的错误管理机制。捕获特定异常(如 StandardError
子类)、使用自定义异常和 retry
可增强代码健壮性。结合块方式和 ensure
,可以安全管理资源。注意避免过度捕获异常,合理使用异常信息,确保代码清晰高效。
如果你有具体问题或需要更详细的示例,请告诉我!