回覆列表
  • 1 # 會點程式碼的大叔

    如果 Java 方法不能按照正常的流程執行,那麼可以透過另外一種途徑退出:丟擲一個封裝了錯誤資訊的物件,這個就是 Java 的異常;當發生異常時,後面的程式碼無法繼續執行,而是由異常處理器繼續執行。

    01. 異常的分類

    Throwable 是所有異常的超類,下一級可以分為 Error 和 Exception :

    1. Error

    Error 是指 Java 執行時系統內部的錯誤,或者說它代表了 JVM 本身的錯誤,通常都是比較嚴重的錯誤, 比如記憶體溢位, 虛擬機器錯誤等等;

    Error 通常和硬體或 JVM 有關,和程式本身無關,所以不能被程式碼捕獲和處理。

    2. Exception

    我們經常說的異常是指 Exception,又可以分成執行時異常和檢查異常。

    RuntimeException:執行時異常,這類異常在編譯期間不強制程式碼捕捉,但是可能在在 JVM 執行期間丟擲異常;出現此類異常,通常是程式碼的問題,所以需要修改程式避免這類異常。常見的執行時異常,比如:NullPointerException、ClassCastException 等等。

    CheckedException:檢查異常,這種異常發生在編譯階段,Java 編譯器會強制程式碼去捕獲和處理此類異常;比如:ClassNotFoundException、IllegalAccessException 等等。

    02. 異常的處理方法

    捕獲異常使用 try...catch 語句,把可能發生異常的程式碼放到 try {...} 中,然後使用 catch 捕獲對應的異常;

    我們也可以在程式碼塊中使用 Throw 向上級程式碼丟擲異常;

    在方法中使用 throws 關鍵字,向上級程式碼丟擲異常;

    03. Throw 和 throws 的區別

    Throw 在方法內,後面跟著異常物件;而 throws 是用在方法上,後面跟異常類;

    Throw 會丟擲具體的異常物件,當執行到 Throw 的時候,方法內的程式碼也就執行結束了;throws 用來宣告異常,提醒呼叫方這個方法可能會出現這種異常,請做好處理的準備,但是不一定會真的出現異常。

    04. 如何優雅地處理異常

    不要試圖透過異常來控制程式流程,比如開發一個介面,正確的做法是對入參進行非空驗證,當引數為空的時候返回“引數不允許為空”,而不應該捕捉到空指標的時候返回錯誤提示。

    僅捕獲有必要的程式碼,儘量不要用一個 try...catch 包住大段甚至整個方法內所有的程式碼,因為這樣會影響 JVM 對程式碼進行最佳化,從而帶來額外的效能開銷。

    很多程式設計師喜歡 catch(Exception e),其實應該儘可能地精確地指出是什麼異常。

    不要忽略異常,捕捉到異常之後千萬不能什麼也不做,要麼在 catch{...} 中輸出異常資訊,要麼透過 Throw 或 throws 丟擲異常,讓上層程式碼處理。

    儘量不要在 catch{...} 中輸出異常後,又向上層程式碼丟擲異常,因為這樣會輸出多條異常資訊,而且它們還是相同的,這樣可能會產生誤導。

    不要在 finally{...} 中寫 return,因為 try{...} 在執行 return 之前執行 finally{...} ,如果 finally{...} 中有 return,那麼將不再執行 try{...} 中的return。

  • 2 # 向Rickey提需求

    既然談到了優雅,那我預設是已經瞭解了異常的基本概念以及異常的分類。

    以下純個人看法,如若雷同,就你是抄我的!

    1.在方法裡面跳出定義的異常(該異常有具體的檢查性)

    public void test() throws Exception_A Exception_B

    {

    //建議使用方式

    }

    public void test() throws exception

    {

    //low B 方式

    }

    為什麼low?檢查性異常的目的是什麼?宣告方法可能丟擲的檢查性異常,如果只有這樣的檢查性異常,那應該包裝到自己的異常中並且新增資訊。

    2.千萬別捕Throwable類

    因為Java Error也是Throwable的子類,而Error是虛擬機器本身無法處理的,對於某些虛擬機器的實現,虛擬機器可能甚至不會再Error上去用catch。總而言之這是一個更嚴重的麻煩。

    3.不想寫了,也不知道有沒有人看。有人看再更。

  • 3 # 冰魄秋雨

    Java中異常提供了一種識別及響應錯誤情況的一致性機制,有效地異常處理能使程式更加健壯、易於除錯。異常之所以是一種強大的除錯手段,在於其回答了以下三個問題:

    什麼出了錯?在哪出的錯?為什麼出錯?

    在有效使用異常的情況下,異常型別回答了“什麼”被丟擲,異常堆疊跟蹤回答了“在哪“丟擲,異常資訊回答了“為什麼“會丟擲,如果你的異常沒有回答以上全部問題,那麼可能你沒有很好地使用它們。有三個原則可以幫助你在除錯過程中最大限度地使用好異常,這三個原則是:

    具體明確提早丟擲延遲捕獲

    為了闡述有效異常處理的這三個原則

    明確異常

    捕 獲異常時儘量明確也很重要。例如:如下可以透過重新詢問使用者檔名來處理FileNotFoundException,對於 EOFException,它可以根據異常丟擲前讀取的資訊繼續執行。

    File prefsFile = new File(prefsFilename); try{ readPreferences(prefsFile); }catch (FileNotFoundException e){ // alert the user that the specified file // does not exist }catch (EOFException e){ // alert the user that the end of the file // was reached }

    透過使用多個catch塊來給使用者提供捕獲到異常的明確資訊。舉例來說:如果捕獲了FileNotFoundException,它可以提示使用者指定另一 個檔案,某些情況下多個catch塊帶來的額外編碼工作量可能是非必要的負擔,但在這個例子中,額外的程式碼的確幫助程式提供了對使用者更友好的響應。

    儘早丟擲異常

    異常堆疊資訊提供了導致異常出現的方法呼叫鏈的精確順序,包括每個方法呼叫的類名,方法名,程式碼檔名甚至行數,以此來精確定位異常出現的現場。

    java.lang.NullPointerException

    at java.io.FileInputStream.open(Native Method)at java.io.FileInputStream.<init>(FileInputStream.java:103)at jcheckbook.JCheckbook.readPreferences(JCheckbook.java:225)at jcheckbook.JCheckbook.startup(JCheckbook.java:116)at jcheckbook.JCheckbook.<init>(JCheckbook.java:27)at jcheckbook.JCheckbook.main(JCheckbook.java:318)

    以 上展示了FileInputStream類的open()方法丟擲NullPointerException的情況。不過注意 FileInputStream.close()是標準Java類庫的一部分,很可能導致這個異常的問題原因在於我們的程式碼本身而不是Java API。所以問題很可能出現在前面的其中一個方法,幸好它也在堆疊資訊中打印出來了。

    不幸的是,NullPointerException是Java中資訊量最少的(卻也是最常遭遇且讓人崩潰的)異常。它壓根不提我們最關心的事情:到底哪裡是null。所以我們不得不回退幾步去找哪裡出了錯。

    透過逐步回退跟蹤堆疊資訊並檢查程式碼,我們可以確定錯誤原因是向readPreferences()傳入了一個空檔名引數。既然readPreferences()知道它不能處理空檔名,所以馬上檢查該條件:

    public void readPreferences(String filename) throws IllegalArgumentException{ if (filename == null){ throw new IllegalArgumentException("filename is null"); } //if //...perform other operations... InputStream in = new FileInputStream(filename); //...read the preferences file...}

    透過提早丟擲異常(又稱"迅速失敗"),異常得以清晰又準確。堆疊資訊立即反映出什麼出了錯(提供了非法引數值),為什麼出錯(檔名不能為空值),以及哪裡出的錯(readPreferences()的前部分)。這樣我們的堆疊資訊就能如實提供:

    java.lang.IllegalArgumentException: filename is nullat jcheckbook.JCheckbook.readPreferences(JCheckbook.java:207)at jcheckbook.JCheckbook.startup(JCheckbook.java:116)at jcheckbook.JCheckbook.<init>(JCheckbook.java:27)at jcheckbook.JCheckbook.main(JCheckbook.java:318)

    另外,其中包含的異常資訊("檔名為空")透過明確回答什麼為空這一問題使得異常提供的資訊更加豐富,而這一答案是我們之前程式碼中丟擲的NullPointerException所無法提供的。

    透過在檢測到錯誤時立刻丟擲異常來實現迅速失敗,可以有效避免不必要的物件構造或資源佔用,比如檔案或網路連線。同樣,開啟這些資源所帶來的清理操作也可以省卻。

    延遲捕獲

    菜鳥和高手都可能犯的一個錯是,在程式有能力處理異常之前就捕獲它。Java編譯器透過要求檢查出的異常必須被捕獲或丟擲而間接助長了這種行為。自然而然的做法就是立即將程式碼用try塊包裝起來,並使用catch捕獲異常,以免編譯器報錯。

    問 題在於,捕獲之後該拿異常怎麼辦?最不該做的就是什麼都不做。空的catch塊等於把整個異常丟進黑洞,能夠說明何時何處為何出錯的所有資訊都會永遠丟失。把異常寫到日誌中還稍微好點,至少還有記錄可查。但我們總不能指望使用者去閱讀或者理解日誌檔案和異常資訊。讓readPreferences()顯示錯誤資訊對話方塊也不合適,因為雖然JCheckbook目前是桌面應用程式,但我們還計劃將它變成基於HTML的Web應用。那樣的話,顯示錯誤對話方塊顯然不是個選擇。同時,不管HTML還是C/S版本,配置資訊都是在伺服器上讀取的,而錯誤資訊需要顯示給Web瀏覽器或者客戶端程式。

    readPreferences()應當在設計時將這些未來需求也考慮在內。適當分離使用者介面程式碼和程式邏輯可以提高我們程式碼的可重用性。

    那麼在最外層捕獲異常,統一處理:

    File prefsFile = new File(prefsFilename); try{ readPreferences(prefsFile); }catch (FileNotFoundException e){ // alert the user that the specified file // does not exist }catch (EOFException e){ // alert the user that the end of the file // was reached }Spring統一處理異常

    如果是是Spring框架,可以使用Spring的統一異常處理機制,其他異常統統都丟擲異常

    public void queryUser() throws Exceptions{}@RestControllerAdvicepublic class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public Rsp handleDefaultException(Exception exception) {if (exception instanceof HttpRequestMethodNotSupportedException) {System.out.println("不支援該請求方式,只直至GET,POST");}}}

    這裡有一個目的就是在最上層處理業務邏輯的異常,這一條規則是明確異常(可以自定義異常來明確),儘早丟擲異常,延遲捕獲異常。

  • 中秋節和大豐收的關聯?
  • 為啥有的寶媽產後看著年輕,有的卻顯老?