服务端相关 / 22 Ruby 异常捕获

Ruby异常捕获

当Ruby的代码运行异常时,会抛出异常,我们在开发时随时可能会发生异常,本章节中让我们来了解Ruby中的异常。

1. 什么是异常

在 Ruby 中异常是一种特殊的对象,它是 Exception 实例或这个类的子类。下面引用《Programming Ruby》书中的一个图片。

图片描述

默认情况下,当程序发生异常,Ruby程序会立即终止。不过我们可以使用异常处理机制来处理程序中遇到的异常,它是一个代码块,当异常发生时,将执行异常处理的代码。

2. raise抛出异常

除了编程异常出现的异常外,我们可以使用raise来强制抛出一个异常。

实例:

def raise_exception
  puts "before raise exception"
  raise "This is a exception"
  puts "after raise exception"
end
raise_exception

# ---- 输出结果 ----
before raise exception
Traceback (most recent call last):
	1: from test.rb:7:in `<main>'
test.rb:3:in `raise_exception': This is a exception (RuntimeError)

解释:由打印我们可以看到,当执行完"before raise exception"的文字输出后,程序抛出了一个异常,这个异常的名称是我们定义的“This is a exception”。

Tips:默认情况下,raise创建RuntimeError类的异常。

我们也可以通过传入异常的类型,来改变raise异常的类型。

实例:

def inverse(x)  
  raise ArgumentError, 'Argument is not numeric' unless x.is_a? Numeric  
  1.0 / x  
end  
puts inverse(2)  
puts inverse('not a number')  

# ---- 输出结果 ----
0.5
Traceback (most recent call last):
	1: from test.rb:6:in `<main>'
test.rb:2:in `inverse': Argument is not numeric (ArgumentError)

解释: 我们在raise后面增加了需要抛出的异常类型,由输出结果我们可以看到,最后抛出的异常类型为ArgumentError。

3. 异常处理

为了捕获异常处理,我们使用begin-end将可能发生异常的代码封装它之中,并使用rescue告诉我们要处理异常的类型。

让我们捕获一下第一个例子的异常。

实例:

def raise_exception
  puts "before raise exception"
  raise "This is a exception"
  puts "after raise exception"
end

begin
  raise_exception
rescue Security => e
  puts "get the exception"
end

# ---- 输出结果 ----
before raise exception
get the exception

解释:由上面例子我们可以看到,当出现异常时,将立刻执行rescue后面的语句,而异常中断后面的代码不会执行。

Tips:如图显示大多数异常属于 StandardError,默认情况下,Ruby 的异常捕获只捕获StandardError 的异常。

我们也可以将异常对象映射到rescue的后面来只获取指定类型的异常。

实例:

def raise_exception
  puts "before raise exception"
  raise "This is a exception"
  puts "after raise exception"
end

begin
  raise_exception
rescue SecurityError => e
  puts "get the exception"
end

# ---- 输出结果 ----
before raise exception
Traceback (most recent call last):
	1: from test.rb:8:in `<main>'
test.rb:3:in `raise_exception': This is a exception (RuntimeError)

解释:由于异常的类型是 StandardError,所以并不会触发异常捕获。

我们也可以对多种异常类型进行捕获。它的形式如下显示,当异常类型不匹配时,会依次向下进行匹配,如果不发生异常,将执行else下面的语句。

begin  
  # -  
rescue OneTypeOfException  
  # -  
rescue AnotherTypeOfException  
  # -  
else  
  # No exceptions  
end  

实例:

def raise_exception
  puts "before raise exception"
  raise "This is a exception"
  puts "after raise exception"
end

begin
  raise_exception
rescue SecurityError => e
  puts "get the SecurityError"
rescue StandardError => e
  puts "get the StandardError"
end

# ---- 输出结果 ----
before raise exception
get the StandardError

解释:当抛出异常后,首先将异常类型和 SecurityError 进行对比,发现不符合继续查找下一个,第二个异常类型 StandardError 和当前异常相符合,于是执行了它下面的语句。

4. retry

在捕获到异常并执行rescue下的语句时,我们还可以使用retry来重新执行发生异常的代码。

num = 0

begin
  puts "current num = #{num}"
  raise if num < 3
  puts "finished!"
rescue 
  num += 1
  puts 'retry!'
  retry
end

# ---- 输出结果 ----
current num = 0
retry!
current num = 1
retry!
current num = 2
retry!
current num = 3
finished!

解释:当num小于 3 时代码运行会一直触发异常,每次当异常发生时,执行num的累加操作并执行retry,直到 num 等于 3 不抛出异常代码结束。

5. 小结

本章节我们学习了什么是异常,使用raise强制抛出异常,使用begin-end+rescue来捕获异常,以及使用retry重新运行出现异常的代码。