第1章           错误处理与例外

当没有例外处理时,程序出错,就会显示出下面这样的错误消息并结束:

> ruby test.rb

test.rb:2:in ‘open’: No such file or directory – “no/file” (Errno: ENOENT)

from test.rb:2:in ‘foo’

from test.rb:6:in ‘bar’

from test.rb:10

消息格式如下:

文件名:行号:in ‘方法名’: 错误消息 (例外类)

      from 调用方法的位置

      …

 

例外处理的写法

begin

  有可能发生例外的处理动作

rescue

  例外发生时的处理措施

ensure

  无论例外发生与否都坚持要执行的动作

end

 

begin

  有可能发生例外的处理动作

rescue => 用来存放例外对象的变量

  例外发生时的处理措施

ensure

  无论例外发生与否都坚持要执行的动作

end

即使没有指定变量名称,例外对象也会自动存放到下表中所列出的变量里,不过明确指定变量名称程序会比较好懂。

例外发生时会自动设定的变量

变量

意义

$!

最后发生的例外(例外对象)

$@

最后例外所发生的位置相关信息

例外对象的方法

方法名

意义

class

例外类别

message

例外的消息

backtrace

例外的发生位置信息($@等同于$!.backtrace)

 

重新执行

在rescue语句块里可以使用retry语句重新执行begin语句块的动作。

file = ARGV[0]

begin

  io = open(file)

rescue

  sleep 10

  retry

end

data = io.read

io.close

每隔10秒重新尝试open方法,成功时则返回文件内容。但若文件一直打不开,会形成无穷循环。

 

rescue修饰符

运算式1 rescue 运算式2

 

n = Integer(val) rescue 0

当Integer方法接收数字字符串时,会将该字符串所代表的数值返回。如果val不是数值的话,就会发生例外,而“=”的右侧整体的值会返回0。

像这样,对于不需要特别复杂的处理,经常用来设定默认值。

 

当例外处理的begin~end范围就是整个方法本身时,可以省略begin和end,只写rescue与ensure语句块。

在类定义里也可以写rescue和ensure语句块。但是在类定义发生例外时,例外发生位置之后的所有方法定义会被跳过,所以一般的程序不会这样用。

 

当不只一种例外可能发生,而想要分开处理这些例外时,可以使用多个rescue语句块来处理。

begin

  有可能发生例外的处理动作

rescue Exception1, Exception2 => 变量

  Exception1或Exception2例外的处理措施

rescue Exception3 => 变量

  Exception3例外的处理措施

rescue => 变量

  其他例外的处理措施

end

指定类名称,就可以捕捉预想的例外。

file1 = ARGV[0]

file2 = ARGV[1]

io = nil

begin

  io = open(file1)

rescue Errno::ENOENT, Errno::EACCES

  io = open(file2)

end

在begin~end内部才开始使用的变量,无法在begin~end之外访问,所以应当事先将nil存进去。

 

所有例外都是Exception类的子类。

rescue语句块所指定的例外种类,是例外的类名称。

在rescue语句块没有指定例外类时,会捕捉StandardError与其子类。

rescue语句块会捕捉例外类,实际上还会捕捉所有该类的子类。所以自己要定义各种例外类时,一般都会先继承StandardError类,接着再往下继承各种例外类。

class MyError < StandardError; end

class MyError1 < MyError; end

class MyError2 < MyError; end

 

自己想要让例外发生时,则使用raise方法。这个方法用在自己想要在某些条件下主动产生出新的例外,或者再次引发之前捕捉到的例外,将例外交给调用来源时。

raise有下面4种形式:

raise 消息

引发RuntimeError例外。指定的字符串会被当作新的例外对象的消息。

raise 例外类

引发指定的例外。

raise 例外类, 消息

引发指定的例外,并且指定的字符串会被当作新的例外对象的消息。

raise

写在rescue语句块外部,则引发RuntimeError例外;写在rescue语句块内,则可以自动再次引发最后发生的例外($!)。

 

catch与throw

虽然不是例外,不过还有catch和throw方法可以用来控制程序执行的流程。

指定throw的第2个实参时,这会成为catch的返回值。

def test_throw

  throw :test

end

puts “test start”

catch (:test){

  puts “before test_throw()”

  test_throw()

  puts “after test_throw()”

}

puts “test end”

输出:

test start

before test_throw()

test end