從事Java
開發的小夥伴對於「異常」應該不陌生,由於天天都會遇到很多異常,或捕獲,或拋出。那究竟什麼是異常?異常即非正常的,不一樣於日常、通常化的狀況
。在平時生活中,醫生會說你身體的某個部位有異常,該異常會有什麼什麼的影響,是由某某緣由引發的;我天天都準時打卡,按時上下班,那麼我本月的考勤是正常的,反之,但凡是有遲到、曠工、早退的狀況之一的,我本月的考情就會有異常。java
而在程序中,代碼在運行中若是出現運行錯誤,程序會終止運行,這時因爲錯誤致使程序運行終止的狀況就是程序出現了異常。異常並非指語法錯誤,由於若是語法錯了,編譯就通不過,不會產生JVM可以識別的字節碼文件,是無法運行起來的,因此只有運行中的程序纔會有異常一說。程序員
異常處理是衡量一門語言是否成熟的標準之一,C
系列的語言諸如:Java
、C++
、C
等都支持異常處理,有本身的一套異常處理機制。異常處理機制可讓程序有更好的容錯性,使代碼更加健壯;但C
語言卻沒有異常處理機制,C
程序員通常都是利用方法的返回值來實現異常處理,使用 if + 條件
來判斷正常和異常狀況,使用特定返回值來表示異常狀況。數據庫
引入異常處理機制能夠解決的問題有:數組
經過一下這個案例來講明引入異常以前的處理方式:網絡
// 汽車 class Car { // 汽車是否正常運行 public static final boolean IS_OK = true; // 汽車運行 public boolean run(int wheelNum) { if (wheelNum == 4) { return true; } return false; } } // 上班族 class Officer { private Car car; public Officer(Car car) { this.car = car; } // 開車去上班 public boolean goWorkByCar(int wheelNum) { if (car == null) { // 只能選擇其餘出行方式了 goWorkByOther(); return false; } boolean result = car.run(wheelNum); if (result) { System.out.println("升職,加薪,迎娶白富美"); return true; } System.out.println("遲到,扣薪,領導每天噴"); return false; } // 其餘方式上班 public void goWorkByOther() { System.out.println("其餘方式上班"); } } // 運行demo public class WorkDemo { public static void main(String[] args) { Car car = new Car(); Officer officer = new Officer(car); officer.goWorkByCar(4); } }
上述案例中,只是簡單粗暴的把汽車的狀態分爲true
和false
兩種,細化不夠,不能體現出汽車的詳細狀態;業務邏輯也很侷限,若是要拓展,就要花費更大的成本。ide
針對於上述的問題,Java
基於面向對象的思想提出瞭解決方案:性能
Java 異常體系:Java API
文檔中的詳細介紹以下this
JVM
相關的不可修復的錯誤,如:系統崩潰、內存溢出、JVM
內部錯誤等,由JVM
拋出,咱們通常狀況下不須要處理,幾乎其全部的子類都是以「Error」
做爲類名後綴;好比:StackOverflowError,當應用程序遞歸太深而發生內存溢出時,就會拋出該錯誤。「Exception」
做爲類名的後綴;Java
體系中,Throwable類是全部錯誤和異常的父類;當出現了沒見過的異常時,能夠將異常類的類名拿到Java API
文檔中去查找,經過文章介紹便可得到異常的詳細信息,以及其在Java
中的繼承、實現體系;常見的Exception
有:spa
null
的時候,對該對象作操做時會出現該異常;Java 的異常詳解:3d
public class ExceptionDemo { public static void main(String[] args) { Integer.valueOf("laofu"); } }
運行結果以下:
若是出現異常,會馬上中斷運行中的程序,因此必須處理異常,而處理方式有兩種:
出現異常以後,程序會中斷執行,因此異常必須處理;處理的方式有兩種:捕獲和拋出,兩種方式二選一便可。咱們先來運行一個案例來證實:
public static void main(String[] args) { System.out.println("老夫開始啦"); int result = 10 / 2; System.out.println("10 / 2 = " + result); System.out.println("老夫去也"); }
運行結果以下:
老夫開始啦 10 / 2 = 5 老夫去也
經過查看運行結果,是咱們指望的運行結果,代碼運行成功;那麼接下來咱們對上述案例稍做修改,再來看其運行結果如何:
public static void main(String[] args) { System.out.println("老夫開始啦"); int result = 10 / 0; System.out.println("10 / 2 = " + result); System.out.println("老夫去也"); }
運行結果以下:
老夫開始啦 Exception in thread "main" java.lang.ArithmeticException: / by zero at Main.main(Main.java:5)
經過查看運行結果,運行結果並非咱們想要的,代碼中出現了異常,代碼被中斷運行。對比兩次的運行結果,咱們能夠得出結論:出現異常以後,程序會中斷執行,異常必須處理。
try-catch 異常捕獲:使用try-catch捕獲單個異常,語法以下:
try{ 編寫可能會出現異常的代碼 } catch(異常類型 e) { 處理異常的代碼 //記錄日誌/打印異常信息/繼續拋出異常 } 注意:try和catch都不能單獨使用,必須連用
因此能夠對上述出現異常的代碼使用try-catch
來處理:
public class ExceptionDemo { public static void main(String[] args) { System.out.println("老夫開始啦"); try { int result = 10 / 0; System.out.println("10 / 2 = " + result); } catch (ArithmeticException e) { System.out.println("異常信息:" + e.getMessage()); System.out.println("異常對象:" + e.toString()); System.out.println("異常棧追蹤:"); e.printStackTrace(); } System.out.println("老夫去也"); } }
運行結果以下:
老夫開始啦異常信息:/ by zero 異常對象:java.lang.ArithmeticException: / by zero 異常棧追蹤:老夫去也java.lang.ArithmeticException: / by zero at Main.main(Main.java:6)
經過查看運行結果,不難發現,使用try-catch
以後,程序遇到異常時再也不中斷執行,而是跳過異常代碼及其以後的在try-catch
中的剩餘代碼語句,來到catch
代碼塊中執行咱們定義的異常處理代碼;
在上述案例中是打印出了異常信息,異常對象信息,異常棧追蹤;其中的3個方法都是Throwable類的方法:
String getMessage()
:獲取異常的詳細描述信息;String toString()
:獲取異常的類型、異常描述信息;void printStackTrace()
:打印異常的跟蹤棧信息並輸出到控制檯,但不能在System.out.println()
中使用該方法;其中包含了異常的類型、異常的緣由、異常出現的位置;在開發和調試階段,該方法都頗有用,方便調試和修改;使用try-catch捕獲多個異常:語法以下:
try{ 編寫可能會出現異常的代碼 } catch (異常類型A e){ // 當try中出現A類型異常,就用該catch來捕獲. 處理異常的代碼 //記錄日誌/打印異常信息/繼續拋出異常 } catch (異常類型B e){ // 當try中出現B類型異常,就用該catch來捕獲. 處理異常的代碼 //記錄日誌/打印異常信息/繼續拋出異常 }
注意
catch
語句,只能捕獲一種類型的異常,若是須要捕獲多種異常類型,就得使用多個catch
語句;try-catch
中的代碼在只會出現一種類型的異常,只能一個catch
捕獲,不可能同時匹配多個catch
;catch
語句的代碼中出現異常時,會從上到下依次匹配catch
語句,因此多個catch
語句應該按照從子類到父類的順序依次定義;catch
以後,便不會匹配剩餘的catch
,而是會跳出try-catch
,執行以後的代碼;捕獲多個異常的案例:
運行結果以下:
老夫開始啦 NumberFormatException 老夫去也 java.lang.NumberFormatException: For input string: "laofu" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:580) at java.lang.Integer.parseInt(Integer.java:615) at Main.main(Main.java:7)
拋出異常有兩種方式:
private static int divide(int num1, int num2) throws Exception {}
throw語句:運用於方法內部,拋出一個具體的異常對象,停止方法的執行,其語法格式以下:
throw new 異常類("異常信息");
通常的,當一個方法出現異常的狀況,咱們不知道該方法應該返回什麼時,此時就能夠返回一個錯誤,在catch
語句塊中使用throw
繼續向上拋出異常。return
是返回一個值,throw
是返回一個錯誤,返回給該方法的調用者。好比:String
類的charAt
方法就是一個很好的例子:
throws:
若是每個方法都放棄處理異常都直接經過throws
聲明拋出,最後異常會拋到main
方法,若是此時main
方法還不處理,會繼續拋出給JVM
,JVM
底層的處理機制就是打印異常的跟蹤棧信息;runtime
異常,默認就是這種處理方式。
方法重寫(Override)中的throws:
一同:方法的簽名必須相同。
兩小:
一大:子類方法的訪問權限必須大於等於父類方法的訪問權限。
異常(Exception
)根據其在編譯時期仍是運行時期去檢查異常可分爲:checked異常和runtime異常,
RuntimeException
自身及其子類異常都屬於runtime異常;runtime異常
以外的其餘異常(包括Exception
自身)都屬於checked異常
; Java
中有着不一樣的定義好的異常類,分別表示着某一種具體的異常狀況,在開發中老是有些異常狀況是Java SE
庫中沒有定義好的,此時就能夠根據本身業務的異常狀況來定義異常類;咱們把這樣的異常類稱爲自定義異常類。
自定義異常類的方式:
java.lang.Exception
;java.lang.RuntimeException
;通常在開發中,自定義的異常都是運行時異常。
異常轉譯:位於最外層的業務系統不須要關心底層的異常細節,咱們經過捕獲原始的異常,將其轉換爲一個新的不一樣類型的異常,而後再向上拋出;這個過程稱爲異常轉譯。
在上述例子中:個人車壞了,在catch
中從新拋出一個新的異常(OfficerException
)給個人調用者(老闆),不能把車的異常信息拋給老闆看,由於老闆不關心這些細節,關心的我是否遲到。
異常鏈:把原始異常包裝爲新的異常類,造成多個異常的有序排列;異常鏈因爲更加清楚、準確的定位異常出現的位置;在下述案例中,異常一層層拋出,直至異常被處理,在這個過程當中,異常鏈就產生了:
1.加強的throw : 對比Java 6
和 Java 7
中對於拋出異常的改進來體現
2.多異常捕獲:重寫捕獲多個異常案例來體現
3.自動資源關閉:資源類必須直接或者間接實現java.lang.AutoCloseable
接口
finally
語句塊表示不管如何(也包括髮生異常時)都會最終執行的代碼塊,好比:當咱們在try
語句塊中打開了一些物理資源(磁盤文件/網絡鏈接/數據庫鏈接等),在使用完以後,都得最終關閉打開的資源。
finally的兩種用法:
1. try...finally: 此時沒有catch
來捕獲異常,由於此時根據應用場景會拋出異常,咱們程序員本身不處理;
2. try...catch....finally:程序員本身須要處理異常,最終得手動關閉資源。
須要注意的是:finally不能單獨使用。
finally不執行的狀況:
當只有在try
或者catch
中調用退出JVM的相關方法,此時finally
纔不會執行,不然finally
修飾的代碼塊永遠會執行。好比:System.exit(0); // 退出JVM
finally 和 return
若是finally和return語句同時存在,永遠返回finally中的結果,可是應該避免這種狀況的出現。詳情看以下的案例:
若是finally
和return
語句同時存在,永遠返回finally
中的結果
還有另外一種狀況也頗有趣,一塊兒來看看:
爲何會出現這種狀況呢?首先finally
確定是會被執行的,因此a++
以後a的值變成了14
,可是finally
中沒有返回值,值爲14
的變量a
並無被返回;而後接着執行return a;
這裏的a
的值在方法執行之初就已經肯定了,故返回的值是13
。
處理異常的原則:
try-catch
的存在也會影響性能,儘可能縮小try-catch
的代碼範圍;Java doc
,若是自定義了異常或某一個方法拋出了異常,應該在文檔註釋中詳細說明;NullPointerException
等;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. Error
和Exception
的區別和關係
5. checked
異常和`runtime異常的區別
6. finally
關鍵字及其相關知識
7. finally
和return
的執行順序
8. throw
和throws
的區別
完結。老夫雖然不正經,但老夫一身的才華!