Java异常详解及自定义异常
我已經不用 try catch 處理異常了!太煩人了_51CTO博客_try catch處理什么異常
一、異常的概念
1.定義(什么是異常?)
異常是例外,是一個程序在執行期間發生的事件,它中斷正在執行程序的正常指令流。軟件開發過程中,很多情況都會導致異常的產生,例如對負數開平方根、對字符串做算術運算、操作數超出范圍、數組下標越界等。
2.異常簡單例子
(1)除數為零的例子
/*** @Author: qp* @Time: 2021/8/24 21:11* @Description*/ public class Example6_1 {public static void main(String[] args){int a= 0;System.out.println(5/a);} }輸出結果:由于除數不能為0,所以程序運行時出現了除以0溢出的異常事件。
Exception in thread "main" java.lang.ArithmeticException: / by zeroat com.chapter6.Example6_1.main(Example6_1.java:11)(2)類型轉換錯誤
/*** @Author: qp* @Time: 2021/8/24 21:14* @Description*/ public class Example6_2 {public static void main(String[] args){String str = "jack";System.out.println(str+"年齡是:");String s = "20L";int age = Integer.parseInt(s);System.out.println(age);} }輸出結果為:報出NumberFormatException(數字格式異常),程序提示了消息,可見提示的消息代碼正常執行,而變量age沒有執行,故s轉換為age的時候程序異常了。
jack年齡是: Exception in thread "main" java.lang.NumberFormatException: For input string: "20L"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 com.chapter6.Example6_2.main(Example6_2.java:13)上面的兩個程序拋出了兩種異常,這兩種異常是一個個的異常對象,異常對象的產生取決于產生異常的類型。此時的異常可以概括為:
異常是程序由于各種原因導致無法正常運行的一個事件,這個事件是由一個異常對象來代表的,這個對象的產生取決于產生異常的類型,可能由應用程序本身產生,也可能由Java虛擬機產生。
二、異常類的層次
checked Exception和unchecked Exception的區別:
要想明白Java中checked Exception和unchecked Exception的區別,我們首先來看一下Java的異常層次結構。
?這是一個簡化的Java異常層次結構示意圖,需要注意的是所有的類都是從Throwable繼承而來,下一層則分為兩個結構,Error和Exception。其中Error類層次描述了Java運行時系統的內部錯誤和資源耗盡錯誤,這種錯誤除了簡單的報告給用戶,并盡力阻止程序安全終止之外,一般也米有別的解決辦法了。
(二)unchecked異常和checked異常的區別
??有了上面的認識之后,我們再來看什么是checked異常,什么是unchecked的異常。其實,Java語言規范對這兩個定義十分簡單,將派生于Error或者RuntimeException的異常稱為unchecked異常,所有其他的異常成為checked異常。
1.異常類的層次
2.異常事件類型
異常類都是內置類Throwable的子類。Throwable類有兩個子類:Error(錯誤)和Exception(異常)
- Error(錯誤):通常是災難性的致命錯誤,不是程序(程序猿)可以控制的,如內存耗盡、JVM系統錯誤、堆棧溢出等。應用程序不應該去處理此類錯誤,且程序員不應該實現任何Error類的子類。
- Exception(異常):用戶可能捕獲的異常情況,可以使用針對性的代碼進行處理,如:空指針異常、網絡連接中斷、數組下標越界等。
3.Exception(異常)分類
Exception(異常)又分為兩類:運行時異常和編譯時異常。
(1)運行時異常
RuntimeException為Java虛擬機在運行時自動生成的異常,如被零除和非法索引、操作數超過數組范圍、打開文件不存在等。此類異常的出現絕大數情況是代碼本身有問題應該從邏輯上去解決并改進代碼。
RuntimeException類及其子類稱為非檢查型異常,Java編譯器會自動按照異常產生的原因引發相應類型的異常,程序中可以選擇捕獲處理也可以不處理,雖然Java編譯器不會檢查運行時異常,但是也可以去進行捕獲和拋出處理。RuntimeException類和子類以及Error類都是非受檢異常。
(2)編譯時異常
Exception中除RuntimeException及其子類之外的異常,該異常必須手動在代碼中添加捕獲語句或一直向上拋出,來處理該異常。編譯時異常也稱為受檢異常,一般不進行自定義檢查異常。
4.常見的異常類
| ClassCastException | 類型轉換異常 |
| ArrayIndexOutOfBoundsException | 數組越界異常 |
| NegativeArraySizeException | 指定數組維數為負值異常 |
| ArithmeticException | 算數異常 |
| InternalException | Java系統內部異常 |
| NullPointerException | 空指針異常 |
| IllegalAccessException | 類定義不明確所產生的異常 |
| IOException | 一般情況下不能完成I/O操作產生的異常 |
| EOFException | 打開文件沒有數據可以讀取的異常 |
| FileNotFoundException | 在文件系統中找不到文件路徑或文件名稱時的異常 |
| ClassNotFoundException | 找不到類或接口所產生的異常 |
| CloneNotSupportedException | 使用對象的clone方法但無法執行Cloneable所產生的異常 |
5.異常類的常用方法
- getMessage():返回Throwable對象的詳細信息,如果該對象沒有詳細信息則返回null(常用打印錯誤信息)
- getLocalizedMessage() :返回Throwable的本地化描述,子類可能會覆蓋該方法以便產生一個特定于本地的消息,對于未覆蓋該方法的子類,默認返回調用getMessage()的結果
- printStackTrace():將Throwable和它的跟蹤情況打印到標準錯誤流(常用打印詳細追蹤錯誤信息)
- toString():返回Throwable對象的類型與性質
三、Java異常處理過程
1.什么叫異常處理機制?
前面定義過:異常是程序由于各種原因導致無法正常運行的一個事件,這個事件是由一個異常對象來代表的,這個對象的產生取決于產生異常的類型,可能由應用程序本身產生,也可能由Java虛擬機產生。異常事件是 Throwable 類或其子類的實例(異常對象),異常處理機制就是當一個方法出現異常后便拋出一個異常對象,該對象中包含有異常信息,調用這個對象的方法可以捕獲到這個異常并可以對其進行處理,異常處理機制并不能真正的修復程序的bug,只能說是防止程序崩潰,要修復程序的bug時需要我們在catch語句中對異常進行一些處理。
- 定義:Java是采用面向對象的方式來處理異常的。處理過程:
(1)拋出異常:在執行一個方法時,如果發生異常,則這個方法生成代表該異常的一個對象,停止當前執行路徑,并把異常對象提交給運行時系統(JRE)。
(2)捕獲異常:JRE得到該異常后,尋找相應的代碼來處理該異常。JRE在方法的調用棧中查找,從生成異常的方法開始回溯,直到找到相應的異常處理代碼為止。 - 限制:Exception 中除 RuntimeException 及其子類之外的異常一般需要進行異常處理
2.Java異常關鍵字
Java 的異常處理是通過 5 個關鍵詞來實現的:try、catch、throw、throws 和 finally
? try – 用于監聽。將要被監聽的代碼(可能拋出異常的代碼)放在try語句塊之內,當try語句塊內發生異常時,異常就被拋出。
? catch – 用于捕獲異常。catch用來捕獲try語句塊中發生的異常。
? finally – finally語句塊總是會被執行。
? throw – 用于拋出異常。
? throws – 用在方法簽名中,用于聲明該方法可能拋出的異常。
3.異常處理機制類型
- 在Java應用中,異常的處理機制分為聲明異常,拋出異常和捕獲異常。
- 異常處理機制類型與異常關鍵字的對應關系
4.Java異常處理過程——捕獲異常catch
(1)定義
異常對象被系統沿著方法的調用棧逐層回溯,交給能夠匹配這種異常對象的異常引用,接著對異常對象進行處理,這一過程就是捕獲(catch)異常。
(2)結構語法
try {// 程序代碼塊}catch (ExceptionType1 e){// 對ExceptionType1的處理}catch (ExceptionType2 e){// 對ExceptionType2的處理}...final{// 程序代碼塊 }try語句中存放的是可能發生異常的語句。當異常拋出時,異常處理機制負責搜尋參數與異常類型相匹配的第一個處理程序,然后進入catch語句中執行,此時認為異常得到了處理。如果程序塊里面的內容很多,前面的代碼拋出了異常,則后面的正常程序將不會執行,系統直接catch捕獲異常并且處理
catch語句可以有多個,用來匹配多個異常,捕獲異常的順序與catch語句的順序有關,當捕獲到對應的異常對象時,剩下的catch語句不再進行匹配,因此在安排catch語句的順序時,首先應該捕獲最特殊的異常,然后一般化。catch的類型是Java語言定義的或者程序員自己定義的,表示拋出異常的類型。異常的變量名表示拋出異常的對象的引用,如果catch捕獲并匹配了該異常,那么就可以直接用這個異常變量名來指向所匹配的異常,并且在catch語句中直接引用。部分系統生成的異常在Java運行時自動拋出,也可通過throws關鍵字聲明該方法要拋出的異常,然后在方法內拋出異常對象。
final語句為異常提供一個統一的出口,一般情況下程序始終都要執行final語句,final在程序中可選。final一般是用來關閉已打開的文件和釋放其他系統資源。try-catch-final可以嵌套。
有4種特殊情況,finally塊不會被執行:
- finally語句塊中發生了異常
- 前面的代碼中執行了System.exit()退出程序
- 程序中所在的線程死亡
- 關閉CPU
(3)舉例
- 例1:捕獲異常
程序輸出結果為:
java.lang.ArrayIndexOutOfBoundsException: 0 finally block輸出信息表明程序中捕獲到了一個ArrayIndexOutOfBoundsException類的運行時的異常,是由于main的參數args[]字符串數組沒有接收到任何輸入造成的。
右擊程序包,選中Run As->Run Configurations,打開Run Configurations界面,在Arguments一欄填充數據“2”,再次運行,運行結果為:
a=5 finally block重新輸入參數“0”,再次以運行此程序,程序輸出結果為:拋出ArithmeticException異常,ArrayIndexOutOfBoundsException異常并沒有被捕獲。
finally block Exception in thread "main" java.lang.ArithmeticException: / by zeroat com.chapter6.Example6_1.main(Example6_1.java:20)可以將程序改為:
public static void main(String[] args) {try {int i =Integer.parseInt(args[0]);int a = 10/i;System.out.println("a="+a);}catch (ArrayIndexOutOfBoundsException e){System.out.println(e);}catch (ArithmeticException e){System.out.println(e);}finally {System.out.println("finally block");}}運行結果為:拋出ArithmeticException
java.lang.ArithmeticException: / by zero finally block- 例2:匹配多個異常的catch子句
考慮到該程序運行時可能產生的異常有:當沒有任何輸入時,會捕獲到ArrayIndexOutOfBoundsException 異常;當輸入值為0時,會捕獲到ArithmeticException 異常,當輸入格式不是整數時,會捕獲NumberFormatException異常。
通常,為捕獲到所有可能出現的異常,可以在處理異常的末尾,加上Exception 類,這樣即可以使所有異常都被捕捉到,也可以防止想捕獲具體異常時被它提前捕獲。
- 例3:多重捕獲
運行程序,輸出結果為:可以看出finally子句總能被執行。
有輸入數字串或輸入格式不正確 finally block(4)關于return和finally的關系
- 例1:try中有return
輸出結果為:
one finally block two finally block從程序運行結果可以看出,無論在什么位置添加return,finally子句都會被執行。
- 例2:catch和try中都有return
輸出結果為:
catch語句模塊 finally語句模塊 1當try中拋出異常且catch中有return語句,finally中沒有return語句,java先執行catch中非return語句,再執行finally語句,最后執行return語句。若try中沒有拋出異常,則程序不會執行catch體里面的語句,java先執行try中非return語句,再執行finally語句,最后再執行try中的return語句。
- 例3:finally 中有return
輸出結果為:
catch語句模塊 finally語句模塊 2finally中有return時,會覆蓋掉try和catch中的return。
- 例4:finally中沒有return語句,但是改變了返回值
先來看第一個例子
public class Example6_7 {public static int set() {int a = 1;try {return a;} catch (Exception e) {System.out.println("catch語句");} finally {a = 200;}return a;}public static void main(String[] args) {System.out.println("a="+set());} }可以先猜猜輸出的結果是什么,接著來看第2個例子:
public class Example6_7 {public int a;public Example6_7 set() {try {this.a=10;} catch (Exception e) {System.out.println("catch語句");} finally {System.out.println("finally語句塊");this.a=200;}return this;}public static void main(String[] args) {Example6_7 t=new Example6_7();t.set();System.out.println(t.a);} }可以看到兩段代碼運行后的結果如下:
a=1 finally語句塊 200如果finally中定義的數據是基本數據類型或文本字符串,則在finally中對該基本數據的改變不起作用,try中的return語句依然會返回進入finally塊中之前保存的值;如果finally中定義的數據是是引用類型,則finally中的語句會起作用,try中return語句的值就是在finally中改變后該屬性的值。
(5)使用場景
針對需要如何處理的異常,采用捕獲的方式去處理異常。
5.Java異常處理過程——聲明異常throws
(1)定義
如果一個方法可能會出現異常,但沒有能力處理這種異常,可以在方法聲明處用throws子句來聲明拋出異常。用它修飾的方法向調用者表明該方法可能會拋出異常(可以是一種類型,也可以是多種類型,用逗號隔開)(位置: 寫在方法名 或方法名列表之后 ,在方法體之前。)
(2)結構語法
static void pop() throws Exception1,Exception2,Exception3{//方法體}(3)throws與throw的區別
- throw 在方法體內使用,throws 函數名后或者參數列表后方法體前
- 意義 : throw 強調動作,而throws 表示一種傾向、可能但不一定實際發生
- throws 后面跟的是異常類,可以一個,可以多個,多個用逗號隔開。throw 后跟的是異常對象。
(4)舉例
public class Example6_3 { //定義方法并拋出NegativeArraySizeException異常static void pop() throws NegativeArraySizeException{int[] arr = new int[-5];}public static void main(String[] args){try {//處理異常信息pop();//調用pop方法}catch (NegativeArraySizeException e){System.out.println("pop()方法拋出的異常");//輸出異常消息}} }使用throws關鍵字將異常拋給調用者后,如果調用者不想處理該異常,可以繼續向上拋出,但最終要有能夠處理該異常的調用者。pop方法中沒有處理異常NegativeArraySizeException,而是main函數來處理。
(5)使用場景
- 非檢查異常(運行時異常)可以不使用throws關鍵字來聲明要拋出的異常,編譯也可順利通過,但在運行時會被系統拋出。
- 受檢異常(編譯時異常),必須使用try-catch/throws處理,否則會導致編譯錯誤。
- 僅當拋出了異常,該方法的調用者才必須處理或重新拋出該異常。當方法的調用者無力處理該異常時,應該繼續拋出而不是直接調用方法。
- 調用方法必須遵循一個原則:若覆蓋一個方法,則不能聲明與覆蓋方法不同的異常,聲明的任何異常必須是被覆蓋方法所聲明異常的同類或子類。如下例子:
6.Java異常處理過程——拋出異常throw
(1)定義
Java程序的當前方法或自身不去處理異常,選擇在方法內部使用throw拋出一個Throwable類型的異常。
(2)結構語法
- 關鍵字是throw,throw語句拋出的是異常類對象,因此需要new關鍵字創建這一異常實例,而且只能拋出一個異常實例。
- throw在方法體中,程序會在throw語句后立即終止,它后面的語句執行不到,然后在包含它的所有try塊中(可能在上層調用函數中)從里到外尋找含有與其匹配的catch子句的try塊。
- new 的異常類名稱必須是Throwable或其子類
(3)舉例
- 例1
運行結果:
除數不能為0上面的例子中,b=0,引發了ArithmeticException異常,因此創建了ArithmeticException對象,并由throw語句將異常拋給Java運行時系統,系統尋找匹配的異常處理器catch并運行相應的異常處理代碼,打印輸出”除數不能為0“。try-catch語句結束。
實際上,除數為0等于ArithmeticException,是RuntimeException的子類,運行時異常將由系統自動拋出,不需要使用throw語句,上面的代碼等價于如下代碼:
public class Example6_5 {public static void main(String[] args) {int a = 6;int b = 0;try { System.out.println(a / b);}catch (ArithmeticException e){System.out.println("除數不能為0");}} }- 例2:
程序運行的結果為:
除數不能為0 false可以看到finally語句塊中的return會覆蓋掉catch里面的throw拋出的異常
- 例3:
可以猜測一下程序的運行結果:
除數不能為0 捕獲可以看到同樣finally里面的throw會覆蓋catch里面的return
(4)使用場景
- 針對不知道如何處理的異常,采用拋出的方式去處理。
- throw可拋出系統自定義異常,通常情況下,是用來拋出用戶自定義的異常。
- throw后面不能跟其它代碼塊,否則編譯不能通過,但是可以在finally語句塊中有return語句,finally語句可以讓throw和return共存
四、自定義異常
1.定義
繼承Throwable或者他的子類Exception的用戶自己定義的異常類。前面的內容提到的都是系統有的異常類。
2.在程序中使用自定義異常的步驟
- 創建自定義異常類
- 在方法中通過throw拋出異常對象
- 如果在當前方法中對拋出的異常對象作處理,可以使用try-catch語句塊捕獲拋出異常對象并且處理,否則要在方法的聲明處通過throws關鍵字指明要拋出給方法調用者的異常。
- 在出現異常方法的調用者中捕獲并處理異常
3.結構語法
class UserException extends Exception {UserException(){super();...//其他語句} }throw關鍵字通常用在方法體中,并且拋出一個異常對象
4.舉例
- 例1
輸出結果是:
除數不能為0- 例2
創建自定義異常類AgeException,繼承自類Exception。使用Throw關鍵字拋出異常對象。
input方法用于接收從鍵盤輸入的姓名和年齡,如果輸入為負數,則會拋出AgeException異常并積極捕獲處理,如果輸入正確,則會將姓名和年齡輸出同時結束整個程序。
輸出結果是:
請輸入姓名: jack 請輸入年齡: -20 年齡不能為負數請重新輸入: -34 年齡不能為負數請重新輸入: -5 年齡不能為負數請重新輸入: 11 姓名jack 年齡11例3:
public class InterfaceException extends RuntimeException {private Integer code;private String msg;public InterfaceException(Integer code, String msg) {super(msg);this.code = code;this.msg = msg;}public Integer getCode() {return this.code;}public String getMsg() {return this.msg;}public void setCode(final Integer code) {this.code = code;}public void setMsg(final String msg) {this.msg = msg;}} @PostMapping(value = "/addSeniorMembers")public Result addSeniorMembers(@RequestBody MembersList membersList) {if (ObjectUtil.isNotEmpty(membersList)){return flowRecordService.save(membersList);}else {throw new InterfaceException(1001,"參數錯誤");}}注意:異常在方法內throw拋出后,需要在method上通過throws拋出
5.系統定義的異常與用戶定義的異常區別
系統定義的異常是特定情況出現的問題,而此時用來對可能遇到的問題進行處理。用戶定義的異常是自己覺得可能會出現問題時,需要處理的。這樣避免程序中斷或是出現未知錯誤。
系統異常有兩種一種是運行時異常,一種是普通異常,普通異常要求用戶捕獲或者拋出的,不捕獲或者拋出就會編譯不通過。運行時異常編譯可以通過,但是運行時才顯露出來。
五、Try-With-Resources
-
Java庫中有很多資源需要手動關閉,例如打開的文件、連接的數據庫等。在java7之前都是try-finally的方式關閉資源,try后面總是跟著一個“{”。
-
舉個例子:try-finally方式關閉資源
從上例可以看出,這種實現非常的雜亂冗長。
Java7之后,推出了Try-With-Resources聲明代替之前的方式,try后跟括號"(",括號內的部分稱為資源規范頭。**資源規范頭中可以包含多個定義,通過分號進行分隔。**規范頭中定義的對象必須實現java.lang.AntoCloseable接口,這個接口中有一個close()方法,因此無論是否正常退出try語句塊,這些對象都會在try語句塊運行結束之后調用close方法,從而替代以前的在finally中關閉資源的功能,且不需要冗長的代碼,另外,Try-With-Resources中的try語句可以不包含catch或者finally語句塊而獨立存在。
public class Example6_9 {public static void main(String[] args) {copy("E:/MyFirst.java","F:/First.Java");//復制文件}private static void copy(String src, String des) {try (InputStream in = new FileInputStream(src);OutputStream out = new FileOutputStream(des)) {byte[] buff = new byte[1024];//創建一個長度為1024字節的字節數組int n;//從輸入流一次最多流入buff.length個字節的數據到buff中,直到文件末尾結束while ((n=in.read(buff))>=0){//將數組buff中的數據從0位置開始,長度為n的字節輸出到輸出流中out.write(buff,0,n);}}catch (IOException e){e.printStackTrace();}} }六、異常的使用原則
Java異常強制用戶去考慮程序的健壯性和安全性,異常處理不用來控制程序的正常流程,其主要作用是捕獲程序在運行時發生的異常并進行相應的處理。編寫代碼處理某個異常時可遵循以下的一些原則:
七、三層架構異常處理
1、dao層不捕獲異常、不拋出異常:spring框架將底層的數據庫checked異常封裝成unchecked異常了
2、service層捕獲異常,并拋出自定義unchecked異常,拋出的異常定義狀態碼:checked異常默認情況事務不會回滾?.? (如果不拋出異常事務不會回滾)
3、controller層捕獲unchecked異常, 處理返回結果.
4、exceptionHandler中統一處理所有沒有捕獲異常
總結
以上是生活随笔為你收集整理的Java异常详解及自定义异常的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AKAI Professional MP
- 下一篇: java 自定义错误代码_Java 自定