Ruby異常是一個(gè)對(duì)象,該類的異?;蚝蟠膶?shí)例。它代表一些特殊的條件。
在Ruby程序出現(xiàn)錯(cuò)誤時(shí),會(huì)引發(fā)異常行為。 默認(rèn)情況下,Ruby程序在拋出異常時(shí)終止。
我們可以在Ruby中聲明一些異常處理程序。 異常處理程序是一些代碼塊,當(dāng)一些其他代碼塊發(fā)生異常時(shí),該塊被執(zhí)行。
異常的情況以兩種方式處理。 可以終止程序或處理異常。 如果要處理異常,可以提供一個(gè)rescue子句,這樣程序控制就可以流向rescue子句。
當(dāng)異常提出但未處理時(shí),全局變量$!包含當(dāng)前異常,$@包含當(dāng)前異常的回溯。
Ruby預(yù)定義的類,如Exception及其子項(xiàng)可以用來處理程序的錯(cuò)誤。 在Ruby異常層次結(jié)構(gòu)中,大多數(shù)子類都擴(kuò)展了ClassError類。 這些是正常的異常。
異常的內(nèi)置子類如下:
示例
def raise_exception
puts 'I am before the raise.'
raise 'oops! An error has occured'
puts 'I am after the raise'
end
raise_exception
執(zhí)行上面代碼,輸出結(jié)果如下 -
F:\worksp\ruby>ruby exception-raise.rb
I am before the raise.
exception-raise.rb:6:in `raise_exception': oops! An error has occured (RuntimeError)
from exception-raise.rb:9:in `<main>'
F:\worksp\ruby>
raise方法來自內(nèi)核模塊。
為了處理異常,引發(fā)異常的代碼包含在begin-end塊中。 使用rescue子句,我們可以說明我們要處理的異常類型。
示例:
#!/usr/bin/ruby
#file: exception-raise_rescue.rb
def raise_and_rescue
begin
puts 'Before the raise.'
raise 'An error occured.'
puts 'After the raise.'
rescue
puts 'Code rescued.'
end
puts 'After the begin block.'
end
raise_and_rescue
執(zhí)行上面代碼,得到以下結(jié)果 -
F:\worksp\ruby>ruby exception-raise_rescue.rb
Before the raise.
Code rescued.
After the begin block.
F:\worksp\ruby>
在上面的例子中,中斷的代碼不能完全運(yùn)行。 異常處理代碼在開始結(jié)束塊(begin-end)之后恢復(fù)。
如果在rescue子句中未定義任何參數(shù),則該參數(shù)默認(rèn)為StandardError。 每個(gè)rescue子句指定多個(gè)異常捕獲。 如果raise沒有使用任何參數(shù),則可能會(huì)重新提出異常。
rescue子句寫在begin/rescue內(nèi),如果不是一個(gè)由rescue子句可處理的異常則將由下一個(gè)異常處理。
begin
code..
rescue OneTypeOfException
code..
rescue AnotherTypeOfException
code..
else
# Other exceptions
end
在開始(begin)塊中,每個(gè)具有引發(fā)異常的rescue子句將依次與每個(gè)參數(shù)進(jìn)行比較。 當(dāng)在rescue子句中指定的錯(cuò)誤類型和異常名稱相同或是該異常的超類時(shí),則將匹配它。 如果沒有異常,完成了begin語句的主體則執(zhí)行else子句。 如果發(fā)生異常,則不會(huì)執(zhí)行else子句。
異常對(duì)象是普通對(duì)象。rescue子句中的變量可以保存被拯救的異常。
示例:
begin
raise 'an exception'
rescue ZeroDivisionError => e
puts "Exception Class: #{ e.class.name }"
puts "Exception Message: #{ e.message }"
puts "Exception Backtrace: #{ e.backtrace }"
end
Exception類定義了兩種方法用于返回有關(guān)異常的詳細(xì)信息。 message方法返回一個(gè)定義錯(cuò)誤說明的字符串。 backtrace方法返回一個(gè)字符串?dāng)?shù)組,表示在引發(fā)異常的時(shí)刻調(diào)用堆棧。
通常在rescue子句中,捕獲異常并在begin塊阻止繼續(xù)執(zhí)行代碼。 使用retry語句,捕獲塊代碼可以在捕獲異常之后還可以從begin塊處恢復(fù)。
語法:
begin
code....
rescue
# capture exceptions
retry # program will run from the begin block
end
示例
#!/usr/bin/ruby
begin
x = Dir.mkdir "alreadyExist"
if x
puts "Directory created"
end
rescue
y = "newDir"
retry
end
上述程序運(yùn)行如下:
步驟1 - 在begin塊中,編寫代碼以創(chuàng)建一個(gè)已經(jīng)存在的目錄。
步驟2 - 這將拋出一個(gè)錯(cuò)誤。
步驟3 - 在rescue區(qū),y被重新分配新值。
步驟4 - retry語句將轉(zhuǎn)到begin塊。
步驟5 - 將創(chuàng)建目錄。
raise語句用于引發(fā)異常。
語法:
raise
或者 -
raise "Error Message"
或者 -
raise ExceptionType, "Error Message"
或者 -
raise ExceptionType, "Error Message" condition
第一個(gè)語法,重新引發(fā)當(dāng)前的異常。 它用于異常處理程序,其中異常在傳遞之前被攔截。
第二個(gè)語法,創(chuàng)建一個(gè)新的RuntimeError異常。 然后這個(gè)異常被引發(fā)為調(diào)用堆棧。
第三個(gè)語法,使用第一個(gè)參數(shù)創(chuàng)建一個(gè)異常,然后將關(guān)聯(lián)消息設(shè)置為第二個(gè)參數(shù)。
第四個(gè)語法,類似第三個(gè)的語法。 在這里可以添加任何條件語句來引發(fā)異常。
示例
#!/usr/bin/ruby
# exceptions-raise-statement.rb
begin
puts 'code before raise.'
raise 'exception occurred.'
puts 'code after raise.'
rescue
puts 'I am rescued.'
end
puts 'code after begin block.'
執(zhí)行上面代碼,得到以下結(jié)果 -
F:\worksp\ruby>ruby exceptions-raise-statement.rb
code before raise.
I am rescued.
code after begin block.
F:\worksp\ruby>
有一個(gè)ensure子句,保證在代碼結(jié)尾處的一些處理。無論是否引發(fā)異常 ensure塊始終運(yùn)行。 它放置在最后一個(gè)rescue子句之后,并且在塊的結(jié)束時(shí)執(zhí)行。
無論是否發(fā)生異常,異常被rescue或代碼被未捕獲的異常終止,ensure塊都將運(yùn)行。
語法
begin
code..
#..raise exception
rescue
#.. exception is rescued
ensure
#.. This code will always execute.
end
示例代碼
#!/usr/bin/ruby
# file : exception-ensure-statment.rb
begin
raise 'Exception'
rescue Exception => e
puts e.message
puts e.backtrace.inspect
ensure
puts "The ensure code will always run"
end
執(zhí)行上面示例代碼,得到以下結(jié)果 -
F:\worksp\ruby>ruby exception-ensure-statment.rb
Exception
["exception-ensure-statment.rb:5:in `<main>'"]
The ensure code will always run
F:\worksp\ruby>
else子句始終存在于rescue子句之后和before子句之前。 如果沒有引發(fā)異常,則只執(zhí)行其他塊。
語法:
begin
code..
#..raise exception
rescue
# .. exception is rescued
else
#.. executes if there is no exception
ensure
#.. This code will always execute.
end
示例
#!/usr/bin/ruby
# file : exception-else-statment.rb
begin
# raise 'A test exception.'
puts "no exception is raised"
rescue Exception => e
puts e.message
puts e.backtrace.inspect
else
puts "else code will be executed as no exception is raised."
ensure
puts "ensure code will run"
end
執(zhí)行上面代碼,得到以下結(jié)果 -
F:\worksp\ruby>ruby exception-else-statment.rb
no exception is raised
else code will be executed as no exception is raised.
ensure code will run
F:\worksp\ruby>
Ruby catch和throw提供了一種在代碼中不需要進(jìn)一步工作的同時(shí)從執(zhí)行中跳出來的方法。
catch定義了一個(gè)標(biāo)有給定名稱的塊。 它用于跳出嵌套代碼。 使用catch語句,塊將被正常執(zhí)行,直到遇到throw。
catch和throw方法比 rescue 和 raise快。 因此,更適合使用。
語法:
throw :lablename
#.. this code will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end
或者 -
throw :lablename condition
#.. this code will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end
示例
#!/usr/bin/ruby
# file : exception-catch-throw.rb
def promptAndGet(prompt)
print prompt
res = readline.chomp
throw :quitRequested if res == "!"
return res
end
catch :quitRequested do
name = promptAndGet("Name: ")
age = promptAndGet("Occupation: ")
# ..
# process information
end
promptAndGet("Name:")
執(zhí)行上面代碼,得到以下結(jié)果 -
F:\worksp\ruby>ruby exception-catch-throw.rb
Name: Maxsu
Occupation: Occupation
Name:Minsu
F:\worksp\ruby>