「JAVA」運行時異常、編譯時異常、自定義異常,經過案例實踐轉譯和異常鏈

Java基礎之異常處理機制

什麼是異常

從事Java開發的小夥伴對於「異常」應該不陌生,由於天天都會遇到很多異常,或捕獲,或拋出。那究竟什麼是異常?異常即非正常的,不一樣於日常、通常化的狀況java

在平時生活中,醫生會說你身體的某個部位有異常,該異常會有什麼什麼的影響,是由某某緣由引發的;程序員

再好比:我天天都準時打卡,按時上下班,那麼我本月的考勤是正常的,反之,但凡是有遲到、曠工、早退的狀況之一的,我本月的考勤就會有異常數據庫

而在程序中,代碼在運行中若是出現運行錯誤,程序會終止運行,這時由於錯誤致使程序運行終止的狀況就是程序出現了異常編程

異常並非指語法錯誤,由於若是語法錯了,編譯就通不過,不會產生JVM可以識別的字節碼文件,是無法運行起來的,因此只有運行中的程序纔會有異常一說。數組

Java 異常體系

異常處理是衡量一門語言是否成熟的標準之一,C系列的語言諸如:JavaC++C 等都支持異常處理,有本身的一套異常處理機制。異常處理機制可讓程序有更好的容錯性,使代碼更加健壯。網絡

因此,引入異常處理機制能夠解決的問題有:ide

  1. 使用方法的返回值來表示異常狀況有侷限性,須要無窮列舉全部的異常狀況;
  2. 異常流程代碼和正常流程代碼混合一塊兒,代碼每每很臃腫,也很複雜性;代碼的可讀性和可維護性都不高;
  3. 隨着系統規模的不斷擴大,代碼很難維護,特別是系統可拓展性差;

經過一下這個案例來講明引入異常以前的處理方式性能

Car.java:spa

Car.java

Officer.java:3d

Officer.java

WorkDemo.java:

WorkDemo.java

上述案例中,只是簡單粗暴地把汽車的狀態分爲truefalse兩種,細化不夠,不能體現出汽車的詳細狀態;業務邏輯也很侷限,若是要拓展,就要花費更大的成本。

針對於上述的問題,Java基於面向對象的思想提出瞭解決方案:

  1. 把不一樣類型的異常狀況使用不一樣的類來表示,不一樣的異常類有共同的父類;
  2. 分離異常流程代碼和正確流程代碼;
  3. 規範異常處理機制,靈活處理異常,能處理就將其捕獲並處理,若是處理不了異常,就將其交給調用者來處理;

Java 異常體系:

Java API文檔中的詳細介紹以下:

JDK中的Java 異常體系

Error表示錯誤,通常指JVM相關的不可修復的錯誤,如:系統崩潰、內存溢出、JVM內部錯誤等,由JVM拋出,通常狀況下不須要處理,幾乎其全部的子類都是以「Error」做爲類名後綴;好比:StackOverflowError,當應用程序遞歸太深而發生內存溢出時,就會拋出該錯誤。

Exception:表示異常,指程序中出現不正常的狀況,異常通常都是須要程序員來處理的(能夠捕獲或者拋出);幾乎其全部的子類都是以「Exception」做爲類名的後綴;

常見的Exception有:

1.NullPointerException:空指針異常,通常當對象爲null的時候,對該對象作操做時會出現該異常;

2.ArrayIndexOutOfBoundsException: 數組的索引越界,操做數組時使用的索引超出了數組的數據範圍會出現;

3.NumberFormatException:數字格式化異常,把非數字的數據類型轉換爲數字類型時使用了非法的轉換對象;

這裏只列出了三種,但Java中的異常類型遠不止這幾種,想要了解更多能夠去查閱JDK文檔。

Throwable:Java 體系中,Throwable類是全部錯誤異常的父類;當出現了沒見過的異常時,能夠將異常類的類名拿到Java API文檔中去查找,經過文章介紹便可得到異常的詳細信息,以及其在Java中的繼承、實現體系;

Java 的異常詳解:

如下是一段運行會出異常的Java 代碼:

ExceptionDemo.java

運行上述代碼案例,運行結果以下:

Java 的異常詳解 案例運行結果

若是出現異常,會馬上中斷運行中的程序,因此必須處理異常,而處理方式有兩種:

  1. throws:當前方法不處理,而是聲明拋出,由該方法的調用者來處理;
  2. try-catch:在當前方法中使用try-catch的語句塊來處理異常;

捕獲異常 try-catch

出現異常以後,程序會中斷執行,因此異常必須處理;處理的方式有兩種:捕獲拋出,兩種方式二選一便可。先來運行一個案例來證實:

沒有異常的案例

運行結果以下:

沒有異常的案例 運行結果

經過查看運行結果,是指望的運行結果,代碼運行成功;那麼接下來對上述案例稍做修改,再來看其運行結果如何:

有異常的案例

運行結果以下:

有異常的案例 運行結果

經過查看運行結果,運行結果並非想要的,代碼中出現了異常,代碼被中斷運行。對比兩次的運行結果,能夠得出結論:出現異常以後,程序會中斷執行,異常必須處理。

try-catch 異常捕獲:使用try-catch捕獲單個異常,語法以下:

try-catch 異常捕獲語法

注意:try和catch都不能單獨使用,必須連用。因此能夠對上述出現異常的代碼使用try-catch來處理:

try-catch 案例

運行結果以下:

try-catch 案例 運行結果

經過查看運行結果,不難發現,使用try-catch以後,程序遇到異常時再也不中斷執行,而是跳過異常代碼及其以後的在try-catch中的剩餘代碼語句,來到catch代碼塊中執行定義的異常處理代碼;

在上述案例中是打印出了異常信息、異常對象信息、異常棧追蹤,其中的3個方法都是Throwable類的方法:

  1. String getMessage():獲取異常的詳細描述信息;
  2. String toString():獲取異常的類型、異常描述信息;
  3. void printStackTrace():打印異常的跟蹤棧信息並輸出到控制檯,但不能在System.out.println()中使用該方法;其中包含了異常的類型、異常的緣由、異常出現的位置;在開發和調試階段,該方法都頗有用,方便調試和修改;

底層的異常處理

而在Java 底層,當代碼出現異常時,JVM會先建立對應的異常類型對象,而後根據異常類型在catch中進行匹配;

若匹配成功,則會把建立好的異常對象賦值給catch中聲明的異常對象;若匹配失敗,則會向上拋出異常。

try-catch 的底層執行分析

使用try-catch捕獲多個異常,語法以下:

try-catch捕獲多個異常 語法

這裏方式其實就是在單個異常捕獲的基礎上添加了多個異常的匹配,使得異常處理更加精細化。

在使用try-catch 時須要注意:

  1. 一個catch語句,只能捕獲一種類型的異常,若是須要捕獲多種異常類型,就得使用多個catch語句;
  2. try-catch中的代碼在只會出現一種類型的異常,只能一個catch捕獲,不可能同時匹配多個catch
  3. 在有多個catch語句的代碼中出現異常時,會從上到下依次匹配catch語句,因此多個catch語句應該按照從子類到父類的順序依次定義;
  4. 一旦匹配上其中一個catch以後,便不會匹配剩餘的catch,而是會跳出try-catch,執行以後的代碼;

捕獲多個異常的案例:

捕獲多個異常的案例

運行結果以下:

捕獲多個異常的案例 運行結果

異常被成功捕獲,再無法瞎蹦噠了。

拋出異常

拋出異常有兩種方式:

  1. throw:用於方法內部,用於給調用者返回一個異常對象,和return同樣會結束當前方法;
  2. throws:運用於方法聲明之上,定義於方法參數以後,表示當前方法不處理異常,而是提醒該方法的調用者來處理拋出的異常(一個或者多個異常);如:private static int divide(int num1, int num2) throws Exception {}

throw語句:運用於方法內部,拋出一個具體的異常對象,停止方法的執行,其語法格式以下:throw new 異常類("異常信息");

通常的,當一個方法出現異常的狀況,不知道該方法應該返回什麼時,此時就能夠返回一個錯誤,在catch語句塊中使用throw繼續向上拋出異常。return 是返回一個值,throw 是返回一個錯誤,返回給該方法的調用者。好比:String類的charAt方法就是一個很好的例子:

String類的charAt方法

throws 語句:若是每個方法都放棄處理異常都直接經過throws聲明拋出,最後異常會拋到main方法,若是此時main方法還不處理,會繼續拋出給JVMJVM底層的處理機制就是打印異常的跟蹤棧信息;runtime異常,默認就是這種處理方式。

方法重寫(Override)中的throws:

一同: 方法的簽名必須相同。

兩小:

1. 子類方法返回類型和父類方法返回類型相同,或是其子類;

2. 子類方法不能聲明拋出新的異常;

一大: 子類方法的訪問權限必須大於等於父類方法的訪問權限。

異常分類

下圖是Java中的異常分類體系,Java 中全部的異常都從Throwable繼承而來,主要分兩大類:Error (錯誤)和異常(Exception)。

Java中的異常分類體系

Throwable體系

異常(Exception)根據其在編譯時期仍是運行時期去檢查異常可分爲:checked異常runtime異常

runtime異常又稱運行時期異常,此類型的異常在運行時期檢查;在編譯時期,運行異常並不會檢測,就不會出現,只有在運行到相關代碼時纔會出現;RuntimeException自身及其子類異常都屬於runtime異常

checked異常又稱編譯時期異常,此類型的異常在編譯時期就會檢查,並且是必須處理的,若是沒有處理,就會致使編譯失敗;除了runtime異常以外的其餘異常(包括Exception自身)都屬於checked異常

jdk 文檔中的異常體系

向下看

詳細的異常分類

自定義異常

Java中有着不一樣的定義好的異常類,分別表示着某一種具體的異常狀況,在開發中老是有些異常狀況是Java SE庫中沒有定義好的,此時就能夠根據本身業務的異常狀況來定義異常類;把這樣的異常類稱爲自定義異常類。

自定義異常類的方式:

受檢查的異常:即checked異常 自定義一個受檢查的異常類須要繼承於java.lang.Exception

運行時異常:即runtime異常 自定義一個運行時期檢查的異常類,須要繼承於java.lang.RuntimeException;通常在開發中,自定義的異常都是運行時異常。

解決開車上班的案例

如今就可使用自定義異常來解決開車上班的案例中的異常問題:

使用自定義異常解決開車上班案例

異常轉譯和異常鏈

異常轉譯:位於最外層的業務系統不須要關心底層的異常細節,經過捕獲原始的異常將其轉換爲一個新的不一樣類型的異常而後再向上拋出;這個過程稱爲異常轉譯

在上述例子中:個人車壞了,在catch中從新拋出一個新的異常(OfficerException)給個人調用者(老闆),不能把車的異常信息拋給老闆看,由於老闆不關心這些細節,關心的是我是否遲到。

異常鏈: 把原始異常包裝爲新的異常類,造成多個異常的有序排列;異常鏈因爲更加清楚、準確的定位異常出現的位置;在下述案例中,異常一層層拋出,直至異常被處理,在這個過程當中,異常鏈就產生了:

異常鏈

Java7的異常新特性

1.加強的throw 對比Java 6Java 7 中對於拋出異常的改進來體現。

加強的throw

2.多異常捕獲: 重寫捕獲多個異常案例來體現。

多異常捕獲

3.自動資源關閉: 資源類必須直接或者間接實現java.lang.AutoCloseable接口

資源自動關閉

finally代碼塊

finally語句塊表示不管如何(也包括髮生異常時)都會最終執行的代碼塊,好比:當在try語句塊中打開了一些物理資源(磁盤文件/網絡鏈接/數據庫鏈接等)時,在使用完以後,都得最終關閉打開的資源。

finally的兩種用法:

1.try...finally 此時沒有catch來捕獲異常,由於此時根據應用場景會拋出異常,程序員本身不處理;

try...finally 的使用

2.try...catch....finally 程序員本身須要處理異常,最終得手動關閉資源。

須要注意的是:finally不能單獨使用

try...catch...finally 的使用

finally不執行的狀況:

當只有在try或者catch調用退出JVM的相關方法此時finally纔不會執行,不然finally修飾的代碼塊永遠會執行。好比:System.exit(0);//退出JVM

finally不執行的狀況

finallyreturn

若是finallyreturn語句同時存在,永遠返回finally中的結果,可是應該避免這種狀況的出現。詳情看以下的案例:

若是finally和return語句同時存在,永遠返回finally中的結果

若是finallyreturn語句同時存在,永遠返回finally中的結果

還有另外一種狀況也頗有趣,一塊兒來看看:

頗有趣的finally例子

爲何會出現這種狀況呢?首先finally確定是會被執行的,因此a++以後a的值變成了14,可是finally中沒有返回值,值爲14的變量a並無被返回;而後接着執行return a;這裏的a的值在方法執行之初就已經肯定了,故返回的值是13

處理異常的原則:

1. 異常只能用於非正常狀況,try-catch的存在也會影響性能,儘可能縮小try-catch的代碼範圍;

2. 須要爲異常提供說明文檔,能夠參考Java doc,若是自定義了異常或某一個方法拋出了異常,應該在文檔註釋中詳細說明;

3. 儘量避免異常的出現,如NullPointerException等;

4. 異常的粒度很重要,應該爲一個基本操做定義一個 try-catch 塊,切忌將幾百行代碼放到一個 try-catch 塊中;

最後,不要在循環中進行異常處理,應該在循環外對異常進行捕獲處理(在循環以外使用try-catch);自定義異常儘可能使用RuntimeException類型的,而且要儘可能避開已存在的異常;

小結

1. 五個關鍵字:try、catch、finally、throw、throws

2. 異常體系的兩個繼承結構:

    Throwable 類有兩個子類:Error和Exception。

    Exception 類有一個特殊的子類:RuntimeException。

    RuntimeException 類及其子類稱之爲runtime異常。

    Exception 類和子類中除了RuntimeException體系的其餘類稱之爲checked異常。

3. 自定義異常類

4. ErrorException的區別和關係

5. checked異常runtime異常的區別

6. finally關鍵字及其相關知識

7. finally和return的執行順序

8. throwthrows的區別

完結,老夫雖不正經,但老夫一身的才華!關注我,獲取更多編程科技知識。

相關文章
相關標籤/搜索