有效的Java編程語言代碼必須遵照捕獲或指定需求,這意味着可能拋出某些異常的代碼必須包含如下任一項:java
try
語句,try
必須爲異常提供處理程序,如捕獲和處理異常中所述。throws
子句,列出異常,如經過方法拋出指定異常中所述。不符合捕獲或指定要求的代碼將沒法編譯。程序員
並不是全部異常都受捕獲或指定要求的約束,爲了理解緣由,咱們須要查看三個基本類別的異常,其中只有一個受要求限制。編程
第一種異常是已檢查的異常,這些都是編寫良好的應用程序應該預料到並從中恢復的異常狀況。例如,假設應用程序提示用戶輸入文件名,而後經過將名稱傳遞給java.io.FileReader
的構造函數來打開該文件,一般,用戶提供現有可讀文件的名稱,所以FileReader
構造對象成功,而且應用程序的執行正常進行。但有時用戶提供不存在的文件的名稱,構造函數拋出java.io.FileNotFoundException
,一個編寫良好的程序將捕獲此異常並通知用戶該錯誤,可能提示更正的文件名。segmentfault
已檢查的異常狀況受捕獲或指定要求的約束,除Error
、RuntimeException
及其子類表示的異常外,全部異常都是通過檢查的異常。數組
第二種異常是錯誤,這些是應用程序外部的異常狀況,應用程序一般沒法預測或恢復。例如,假設應用程序對於輸入成功打開文件,但因爲硬件或系統故障而沒法讀取文件,不成功的讀取將拋出java.io.IOError
,應用程序可能會選擇捕獲此異常,以便通知用戶該問題 — 可是程序打印堆棧跟蹤並退出也可能有意義。編程語言
錯誤不受捕獲或指定要求的約束,錯誤是Error
及其子類表示的異常。函數
第三種異常是運行時異常,這些是應用程序內部的異常狀況,應用程序一般沒法預測或恢復,這些一般表示編程bug,例如邏輯錯誤或API的不當使用。例如,考慮前面描述的應用程序將文件名傳遞給FileReader
的構造函數,若是邏輯錯誤致使將null
傳遞給構造函數,則構造函數將拋出NullPointerException
,應用程序能夠捕獲此異常,但消除致使異常發生的bug可能更有意義。設計
運行時異常不受捕獲或指定要求的約束,運行時異常是RuntimeException
及其子類表示的異常。指針
錯誤和運行時異常統稱爲未經檢查的異常。code
有時,代碼能夠捕獲可能在其中發生的異常,可是,在其餘狀況下,最好讓調用堆棧中進一步的方法處理異常。例如,若是你將ListOfNumbers
類做爲類包的一部分提供,則可能沒法預測包的全部用戶的需求,在這種狀況下,最好不捕獲異常並容許調用堆棧進一步的方法來處理它。
若是writeList
方法沒有捕獲可能在其中發生的已檢查異常,則writeList
方法必須指定它能夠拋出這些異常,讓咱們修改原始的writeList
方法來指定它能夠拋出而不是捕獲它們的異常,這是不能編譯的writeList
方法的原始版本。
public void writeList() { PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt")); for (int i = 0; i < SIZE; i++) { out.println("Value at: " + i + " = " + list.get(i)); } out.close(); }
要指定writeList
能夠拋出兩個異常,請將throws
子句添加到writeList
方法的方法聲明中,throws
子句包含throws
關鍵字,後跟逗號分隔的該方法拋出的全部異常列表,該子句在方法名稱和參數列表以後以及定義方法範圍的大括號以前,這是一個例子。
public void writeList() throws IOException, IndexOutOfBoundsException {
請記住,IndexOutOfBoundsException
是未經檢查的異常,在throws
子句中包含它不是強制性的,你能夠僅寫下面的內容。
public void writeList() throws IOException {
一些程序員認爲捕獲或指定要求是異常機制中的一個嚴重缺陷,並經過使用未經檢查的異常代替已檢查的異常來繞過它,一般,不建議這樣作,未經檢查的異常 — 爭議部分討論什麼時候適合使用未經檢查的異常。
由於Java編程語言不須要捕獲或指定未經檢查的異常的方法(RuntimeException
、Error
及其子類),程序員可能會試圖編寫只拋出未經檢查的異常的代碼,或者讓全部異常子類繼承自RuntimeException
,這兩個快捷方式都容許程序員編寫代碼而沒必要擔憂編譯器錯誤,也沒必要費心去指定或捕獲任何異常。雖然這對程序員來講彷佛很方便,但它會迴避catch
的意圖或指定要求,而且可能會致使其餘人使用你的類時出現問題。
爲何設計人員決定強制一個方法指定能夠在其範圍內拋出的全部未捕獲的已檢查異常?方法能夠拋出的任何Exception
都是該方法的公共編程接口的一部分,那些調用方法的人必須知道方法能夠拋出的異常,以便他們能夠決定如何處理它們,這些異常與該方法的編程接口同樣,也是其參數和返回值的一部分。
下一個問題多是:「若是記錄方法的API很是好,包括它能夠拋出的異常,爲何不指定運行時異常呢?」,運行時異常表示編程問題致使的問題,所以,沒法合理地指望API客戶端代碼從它們恢復或以任何方式處理它們,這些問題包括算術異常,例如除以零;指針異常,例如嘗試經過空引用訪問對象;索引異常,例如嘗試經過索引太大或過小來訪問數組元素。
運行時異常能夠在程序中的任何地方發生,而在典型的程序中,它們能夠很是多,必須在每一個方法聲明中添加運行時異常會下降程序的清晰度,所以,編譯器不要求你捕獲或指定運行時異常(儘管你能夠)。
拋出RuntimeException
的常見作法之一是用戶錯誤地調用方法,例如,方法能夠檢查其中一個參數是否錯誤地爲null
,若是參數爲null
,則該方法可能會拋出NullPointerException
,這是一個未經檢查的異常。
通常來講,不要由於你不但願指定方法能夠拋出的異常而煩惱而拋出RuntimeException
或建立RuntimeException
的子類。
這是底線指南:若是能夠合理地指望客戶端從異常中恢復,則將其做爲已檢查的異常,若是客戶端沒法執行任何操做以從異常中恢復,請將其設置爲未經檢查的異常。