Selenium Web Driver自動化測試(java版)系列上半部分(25) - 異常及捕獲方式

之前學習文件流和數據庫操作時大家經常會看到我按照eclipse的提示在main函數后面加上類似throws xxException這樣的語句,否則程序會提示錯誤。就像這樣:

或是用try...catch包住一些語句,像這樣:

當時我讓大家先不用管,以后再說。其實這種語句就叫做java的異常處理,也是我們這篇要探討的東西。

先說什么是異常。從字面上理解,異常就是不正常的事唄。不光java,生活中也有很多不正常的事。比如你去KTV唱歌,正準備喊麥,結果麥沒聲了。這是個異常。你換了個麥,又正準備喊麥,結果電視屏幕掛了,沒影了。這也是異常。之后你急了,要喊服務員過來修,結果發現按鈕不管用。這還是異常。最后你怒了,直接吵著鬧著要換房間,這次都沒問題了,但你發現你唱跑調了。這些都是異常。

一個java程序也一樣,你執行的時候可能會因為種種原因報錯,有些錯誤并不是因為你的語法錯誤造成的,而是某些外部的因素導致的,這些錯誤就是異常。按照異常的特點,我們把異常分成三類 - 檢查性異常,運行時異常,還有錯誤。

先說第一種,檢查性異常。剛才KTV這個例子中除了最后你唱不上去,其它幾個異常其實壓根與你無關,麥沒聲了電視沒影了能賴你嗎?不能賴。一個java程序也是如此,有些時候并不是你的程序出了bug,而是環境或資源出了bug,這種就叫做檢查性異常。之前介紹文件流時拋的幾個異常就屬于此例,再看一遍:

這兩句代碼之前都講過,意思是依照給定的文件創建一個文件流。你的程序本身并沒有任何問題,但如果構建文件對象時該文件或文件路徑并不存在,那就要拋出異常了。同理還有之前演示過的數據庫操作,如果數據庫或數據庫中某個表不存在也會報錯。所以說這種情況是你無法控制的,代碼寫得再對資源文件出錯了也白搭。而且你會發現eclipse的檢查機制會用紅波浪線提示你此處必須要做出處理,否則編譯的時候會出錯,這種預判也是檢查性異常的優點。java創造者也強制開發人員在類似文件流數據庫這種潛在拋出檢查性異常的地方對異常進行處理。其實,只要你能確保資源或環境的正確,檢查性異常是可以避免的。

第二種叫運行時異常。檢查性異常是在編譯時就會提示,而運行時顧名思義,只在程序編譯完執行時才會提示。這種異常的出現是因為你的程序真的有邏輯上的bug。因為你跑掉了導致唱得不好聽,這個怪不得別人。一個典型例子就是數組越界:

arr數組只有三個位置,你非要訪問第四個(因為數組從0開始),這不就是邏輯錯誤么?程序本身并不顯示紅線,意味著可以編譯。類似的邏輯錯誤還有除以0:

任何數除以0都會得到無窮大的一個數,程序無法給出結果,還是屬于邏輯上的問題。這種情況下把邏輯錯誤修好,運行時異常也是可以避免的。就好比你好好練練,下次唱的時候不跑調就行了。

第三種就叫錯誤。這種異常最厲害,因為它無法避免,而且不易預判。就好比你在KTV連續唱好幾個小時,你勢必口干舌燥嗓子冒煙,后邊唱的質量肯定有所下降。這種情況既不賴人家也不賴你,實在是自然規律。java中因內存耗盡而拋出的異常就屬于這個范疇,你不幸遇上了只能慢慢優化你的代碼,或是仔細查驗別的可能的情況,沒別的好辦法。不過錯誤這種異常畢竟還是少數,我們這篇文章主要還是討論怎么解決前兩種。

捕獲異常的方法有兩種,第一種是在發生異常的地方直接捕獲,用到的是try...catch語句塊。用try包住可能會發生異常的地方,用catch輸出異常信息用于排錯。比如再看上圖這個文件流的例子。當我點擊程序旁邊那個小紅叉時,eclipse會提示我選擇try...catch還是add throws declaration:

選擇try...catch你會發現可能出現異常的地方自動被try包住,catch里面出現一句e.printStackTrace():

e.printStackTrace()就是打印異常信息的意思,參數e的類型是FileNotFoundException。打開java文檔查一下它,我們會看到如下信息:

FileNotFoundException中文翻譯過來就是“文件沒找到異常”,屬于文件流異常中的一種。它是一種異常類,從IOException類繼承來的。介紹文件流的時候說過IO是什么意思,輸入輸出流唄。文件流本身又屬于輸入輸出流的一種,所以它拋出的異常繼承IOException合情合理,好理解吧。而且介紹繼承時說繼承的本質就是青出于藍勝于藍的過程,所以FileNotFoundException擁有比IOException更詳細的信息。

我們再看一下IOException類和所有繼承它的類,會發現除了FileNotFoundException,其實還有好幾種文件流異常,比如文件系統異常,文件流末尾異常等等。截圖不清楚,大家仔細看一下文檔中標注的幾個地方:

再往上看一級,IOException類繼承的是Exception類:

Exception類底下有眾多子類,因為它不光包含IO流異常,而是所有的檢查性異常和運行時異常,但不包括錯誤。最后再往上看一級,Exception繼承的是Throwable類:

Throwable類是所有異常類的最高父類,它只有兩個子類:Exception和Error,Error就是錯誤。前邊說了,Error比較棘手,相對也少見。我們還是主要把注意力放在前兩種上,也就是Exception類中的內容。有了這些基礎我們再翻回去看剛才的例子,eclipse自動檢測出可能拋出的異常是FileNotFoundException,于是在catch語句塊中創建出該類的實例e用來捕獲該異常。程序執行時,如果try語句塊中的程序執行無誤,沒有拋出異常,則catch語句塊不被執行。如果一旦拋出異常,則立即執行catch中的語句,也就是e.printStatckTrace()。由于Exception類中打印異常信息的方法是printStatckTrace(),所以繼承它的IOException以及FileNotFoundException類也都有這個方法,直接用就可以。

注意,如果一旦拋出異常,則立即執行catch中的語句。也就是說,只要你try塊中拋出一個異常,那剩下的代碼就不執行了。比如:

指定文件確實不存在,在FileReader fr = new FileReader(f)處拋出FileNotFoundException異常,于是不再繼續執行try塊剩余的代碼,而是直接執行catch中的e.printStatckTrace()打印異常信息。

有人說我不用eclipse,我的編輯器沒那么聰明,不能自動告訴我潛在拋出哪種異常怎么辦呢?那么多種異常我該選哪個呢?這種情況下最好的辦法就是用父類Exception,直接寫Exception e:

它是父類,包含一切種類的異常,打印出的異常信息自然會告訴你屬于哪種異常,寫程序時你不用管它。

剛才說了,拋異常后就不再繼續執行try塊中剩下的語句,但如果你偏偏想執行那些語句怎么辦呢?還是講文件流的時候,我說一個文件流打開了用完了最后一定要調用close()方法關閉,如果不關閉就會浪費資源。可是,我不能把關閉過程放在try語句里,否則一旦拋異常就不會被關閉了:

所以,java開發人員又加入了第三個語句塊 - finally。一般情況下,不管try塊有沒有異常,finally塊里的所有代碼一定會被執行。我們可以把關閉過程放在finally里:

文件流比較討厭,調用close時也會有檢查性異常,類型是IOException,所以finally塊里又有一個小的try...catch。我知道麻煩,可沒辦法。

我剛才說一般情況下,不管try塊有沒有異常,finally塊里的所有代碼一定會被執行。那什么是不一般的情況呢?看下面兩例,第一個:

第二個:

大家試一下會發現劃出來的語句都不會執行,這兩個就是特殊情況。第一個例子中finally塊里邊的代碼也會發生異常,是個運行時異常。出了異常怎么還能再執行后邊的語句呢對吧?第二個例子System.exit(0)的意思是讓程序退出。都退出了當然就不會再繼續執行后面的語句了。所以,這兩種情況下finally里邊的內容不會執行。但一般情況下很少有人這么寫,誰愿意沒事自己給自己找麻煩呢,對吧?

以上是在可能發生異常的地方直接捕獲。還一種方法是把異常拋回給調用者捕獲,看下面例子:

當eclipse提示我選擇try...catch還是add throws declaration時,我選擇了add throws declaration,程序會直接在readFile()方法名邊上加上"throws IOException",意思是不自己處理,而是向上拋回給調用者。因為調用它的是對象et,所以我們要對et.readFile()加上try...catch語句塊。當然,如果你用的不是eclipse可能沒有自動提示,你就在方法名旁邊直接添加"throws Exception"就行。這就是拋回給調用者的方法,雖然異常會出現在readFile()方法里,但是我們沒在它里面直接捕獲,而是讓它拋出來。那如果et也不想捕獲呢?它繼續往上拋:

它也不處理,那就會讓更高級別的來處理。咱們這個例子里已經到了main()函數了,再往上拋就會交給最高級的java虛擬機來處理。這么做有一個好處,你會發現我們全程不用寫try...catch語句塊了。但是,弊端也有,如果不停上拋會造成整個程序運行效率偏低,而且出錯后終端會打印出來上拋過程中經過的所有類/對象/方法,排錯費點時間。所以,業內大多數人仍然推薦使用第一種方法捕獲。

這篇文章的源代碼是ExceptionException2

本篇知識點及注意事項:
1. 異常分三種: 檢查性異常,運行時異常,錯誤。通常捕獲前兩種。
2. 捕獲異常的方法有兩種,第一種是在發生異常的地方直接捕獲,第二種是把異常拋回給調用者捕獲。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容