異常處理是開發過程中經常要面對的問題,基本所有進階語言都有自己的異常處理系統,ruby也不例外,而且使用起來也非常簡單。
ruby中異常的拋出是使用的raise方法,記住哦,這是個方法,由ruby Kernel提供的,而不是關鍵字,同時ruby也為這個方法提供了一個別名fail,可以用fail代替raise,拋出異常的例子如下:
raise #拋出一個預設的RuntimeError
raise "Some error message" #拋出一個訊息為"Some error message"的RuntimeError
raise ArgumentError #拋出一個無訊息的ArgumentError
raise ArgumentError, "Bad data" #拋出一個訊息為"Bad data"的ArgumentError
raise ArgumentError.new("Bad data") #同上
raise ArgumentError ArgumentError, "Bad data", caller[0] #拋出一個包含訊息的格式為filename:line 或者 filename:line:in 'method' 的異常
以上代碼中的raise可以使用別名fail代替,在沒有明確的給出異常類型時,ruby預設拋出RuntimeError,其中最後一個例子拋出的資訊包含了當前錯誤所在的檔案,行數已經所在的方法的資訊,這些資訊都儲存在caller這個數組中,裡麵包含了方法調用者的相關 資訊,第一個元素包含了方法的調用者的資訊,第二個資訊包含了方法調用者的調用者的資訊,以此類推。這個數組在我們想知道異常是在哪個地方的哪個調用被拋 出的時候非常有用的。
def func1
puts caller #列印調用者資訊
end
def func2
func1 #第六行
end
def func3
func2 #第十行
end
func3 #最終調用者,十三行
#運行結果
#test.rb:6:in `func2'
#test.rb:10:in `func3'
#test.rb:13
從上面的代碼可以看出,caller記錄了每個調用者所在的檔案名稱,行數以及方法。
上面講解了關於ruby異常拋出的方式以及caller數組的作用,接下來我們來瞭解一下ruby中是如何進行異常的捕捉的,在java中,異常的捕捉是在try ... catch當中進行,而ruby則是在begin ... end代碼塊中進行異常的捕捉,在該代碼塊中使用rescue關鍵字進行捕捉異常類型,注意哦,這個是關鍵字,而不是方法。
begin
...... #可能出現異常的代碼
rescue errorType1 #要捕捉的異常類型
...... #處理異常的代碼
rescue errorType2 #要捕捉的異常類型
...... #處理異常的代碼
end
以上代碼就是一個大概的捕捉異常的例子,在begin和end代碼塊中通過rescue進行異常類型的捕捉然後進行適當的處理,可是如果拋出的異常類型並沒有顯示的捕捉如何處理呢?那就是在最後使用else,如下:
begin
...... #可能出現異常的代碼
rescue errorType1 #要捕捉的異常類型
...... #處理異常的代碼
rescue errorType2 #要捕捉的異常類型
...... #處理異常的代碼
else
...... #如果以上代碼類型都沒有捕捉到,則運行該段代碼
end
這時又有一個問題,如果我想擷取異常資訊又該如何做呢?請看下面的代碼:
begin
raise ArgumentError, "Bad data"
rescue => err
puts err
end
通過rescue => variable的方式,就可以將異常儲存為一個variable了。又解決了一個問題,還有什麼問題呢?啊,對了,在java的使用當中,比如使用Connection進行資料庫連接後,最後一定要進行資源的清理,都是在finally塊當中進行的,可是在ruby中又如何進行這些資源的清理呢?看看下面的代碼:
begin
raise ArgumentError, "Bad data"
rescue => err
puts err
ensure
... #執行清理工作
end
從上面代碼我們看到,ruby提供了一個關鍵字ensure,它的作用和java中的finally一樣,無論任何異常,該關鍵字下的代碼都必然會在結束代碼塊前執行。同時,ruby還提供恢複功能,如果在拋出異常並進行異常處理後我們需要進行恢複工作,那就是使用retry就會重新執行代碼塊了。
上面提到,異常的捕捉處理必須在begin-end代碼塊中進行,那是不是無論什麼時候都要書寫begin-end這兩個關鍵字呢?其實也不是,在ruby中,方法實際上就是一個隱式的begin-end代碼塊,所以在方法中進行異常的捕捉和處理,可以省略begin。
本文參考《The Ruby Way》一書所寫,如有不足的地方,請各位指正,謝謝!