Java 超詳細講解異常的處理
1、異常的概念和體系結(jié)構(gòu)
1.1異常的概念
Java中,在程序執(zhí)行過程中發(fā)生的不正常行為稱為異常。比如之前一直遇到的:
(1)算數(shù)異常
System.out.prinntln(10/0);
(2)數(shù)組越界異常
int[] arr={1,2,3}; System.out.println(arr[5]);
(3)空指針異常
int[] arr=null; System.out.println(arr.length());
1.2異常的體系結(jié)構(gòu)及分類
- Throwable:是異常體系的頂尖層,派生出兩個鐘要的子類:而Error、Exception
- Error:指的是Java虛擬機無法解決的嚴重問題
- Exception:就是我們平常所說的異常。程序員可以以通過代碼進行處理,使程序繼續(xù)執(zhí)行。
【異常的分類】
- 運行時異常(受查異常):RuntimeException下的所有異常為運行時異常
- 編譯時異常(非受查異常):IOException、ClassNotFoundException、CloneNotSupportedException為編譯時異常
【注】編譯時出現(xiàn)的語法錯誤,不能稱之為異常。運行時指的是程序已經(jīng)編譯通過得到class文件了,再由JVM執(zhí)行過程中出現(xiàn)的錯誤。
2、異常的處理
在Java中,異常處理主要的五個關(guān)鍵字:throw、try、catch、final、throws
2.1防御式編程
(1)LBYL:look before you leap,在操作之前就做充足的檢查(事前防御型)
boolean ret=false;
ret=登陸游戲();
if(!ret){
處理登陸游戲錯誤;
return;
}
ret=開始匹配();
if(!ret){
處理匹配錯誤;
return;
}
………………
缺陷:正常流程和錯誤處理代碼混在一起,代碼整體顯得比較混亂。
(2)EAFP:it is easier to ask forgiveness than permission,先操作,遇到問題再解決。(事后認錯型)
try{
登陸游戲();
開始匹配();
}catch(登陸游戲異常){
處理登陸游戲異常;
}catch(開始匹配異常){
處理匹配異常;
}
2.2異常地拋出
在Java中,可以借助throw關(guān)鍵字,拋出自定義異常,將錯誤信息告知給調(diào)用者。語法如下:
throw new XXXException("異常產(chǎn)生的原因");
【例】實現(xiàn)一個方法,獲取數(shù)組中任意下標位置的元素
public static int getElement(int[] array, int index){ if(null == array){ throw new NullPointerException("傳遞的數(shù)組為null"); } if(index < 0 || index >= array.length){ throw new ArrayIndexOutOfBoundsException("傳遞的數(shù)組下標越界"); } return array[index]; }
【注】
- throw必須寫在方法的內(nèi)部
- 拋出的對象必須是Exception或Exception的子類對象
- 如果拋出的是編譯時異常,用戶必須處理,否則無法通過編譯
- 如果拋出的是RuntimeException或其子類,可以不用處理,直接交給JVM來處理
- 異常一旦拋出,后面的代碼不會再執(zhí)行
2.3異常的捕獲
異常的具體處理方式,主要有兩種:異常聲明throws和try-catch捕獲處理
printStackTrace()打印異常
(1)異常聲明throws
當前方法不處理異常,提醒方法的調(diào)用者處理異常。
語法格式:
修飾符 返回值類型 方法名(參數(shù)列表) throws 異常類型1,異常類型2...{}
【例】加載指定的配置文件
public class Config { File file; /* FileNotFoundException : 編譯時異常,表明文件不存在 此處不處理,也沒有能力處理,應(yīng)該將錯誤信息報告給調(diào)用者,讓調(diào)用者檢查文件名字是否給錯誤了 */ public void OpenConfig(String filename) throws FileNotFoundException { if(filename.equals("config.ini")){ throw new FileNotFoundException("配置文件名字不對"); } // 打開文件 }
【注】
- throws必須跟在參數(shù)列表的后面。
- 聲名的異常必須是Exception或Exception的子類。
- 方法的內(nèi)部如果拋出多個異常,throws后面必須跟多個異常類型,用逗號隔開。如果拋出的異常之間有父子關(guān)系,直接聲明父類異常即可。
- 調(diào)用聲明拋出異常的方法時,調(diào)用者必須對異常進行處理,或者繼續(xù)使用throws。
(2)try-catch捕獲并處理
throws并沒有對異常真正處理,而是將異常報告給異常方法的調(diào)用者。如果真正要對異常進行處理,需要try-catch。
【語法如下】
try{
// 將可能出現(xiàn)異常的代碼放在這里
}catch(要捕獲的異常類型 e){
// 如果try中的代碼拋出異常了,此處catch捕獲時異常類型與try中拋出的異常類型一致時,或者是try中拋出異常的父類時,就會被捕獲到
// 對異常就可以正常處理,處理完成后,跳出try-catch結(jié)構(gòu),繼續(xù)執(zhí)行后序代碼
}【catch(異常類型 e){
// 對異常進行處理
}finally{
// 此處代碼一定會被執(zhí)行到
}】
// 后序代碼
// 當異常被捕獲到時,異常就被處理了,這里的后序代碼一定會執(zhí)行
// 如果捕獲了,由于捕獲時類型不對,那就沒有捕獲到,這里的代碼就不會被執(zhí)行
注意:
1. 【】中表示可選項,可以添加,也可以不用添加
2. try中的代碼可能會拋出異常,也可能不會
【注意】
- try塊中拋出異常位置后的代碼不會被執(zhí)行
- 如果拋出異常類型與catch類型不匹配,即異常不會被成功捕獲,則需要JVM來處理異常------異常是按照類型來捕獲的。
- rey中可能會拋出多個不同的異常對象,必須用多個catch來捕獲。
- 如果異常之間有父子類關(guān)系,必須子類異常在前catch,父類異常在后catch,否則語法錯誤。
- 可以通過一個catch來捕獲多個異常(不推薦)。
由于Exception類是所有異常的子類,因此可以用這個類型來捕捉所有異常。catch進行類型匹配時,不光會匹配相同類型的異常對象,也會捕捉目標異常類型的子類對象。
(3)finally
在寫程序時,有些特定的代碼,無論程序是否發(fā)生異常,都需要執(zhí)行,比如程序正常打開的資源,有時候必須對資源進行回收。另外,異常會引發(fā)程序的跳轉(zhuǎn),可能導(dǎo)致有些語句執(zhí)行不到,此時需要finally來解決這個問題。
【語法格式】
try{
// 可能會發(fā)生異常的代碼
}catch(異常類型 e){
// 對捕獲到的異常進行處理
}finally{
// 此處的語句無論是否發(fā)生異常,都會被執(zhí)行到
}
// 如果沒有拋出異常,或者異常被捕獲處理了,這里的代碼也會執(zhí)行
【問題】既然finally和try-catch-finally后的代碼都會被執(zhí)行,那為什么還要有finally呢?
當catch沒有捕獲到異常時,此時需要JVM來捕獲異常,程序可能不能正常運行,finally后面的代碼就不會被執(zhí)行。而finally中的代碼一定會被執(zhí)行。
【注】finally中的代碼一定會被執(zhí)行,一般在其中進行資源清理的掃尾工作。
如下代碼:
public static int func(){ try{ return 10; }finally{ return 20; } } //此時返回20;
finally執(zhí)行的時機是方法返回之前(try或者catch中如果有return會在return之前執(zhí)行finally)。但是如果finally中也存在return語句,那么就會執(zhí)行finally中的return,從而不會執(zhí)行到try中原有的return。
2.4異常的處理流程
關(guān)于“調(diào)用棧”:
方法之間存在相互調(diào)用的關(guān)系,可以用“調(diào)用棧”來描述。在JVM中有一塊內(nèi)存空間稱之為:“虛擬機棧”專門存儲方法之間的調(diào)用關(guān)系。當代碼中出現(xiàn)異常的時候,我們就可以使用e.printStackTrace();
的方法查看出現(xiàn)異常代碼的調(diào)用棧。
如果本方法中沒有合適的處理異常的方法,就會沿著調(diào)用棧向上傳遞,如果一直向上傳遞都沒有找到合適的方法,最終會交給JVM來處理,程序就會異常終止。
【程序異常處理的流程】
- 程序先執(zhí)行try中的代碼
- 如果try中的代碼出現(xiàn)異常,就會結(jié)束try,在catch中進行異常匹配
- 如果找到匹配的異常類型,就會執(zhí)行catch中的代碼
- 如果沒有找到,就會將異常向上傳遞到上層調(diào)用者
- 無論是否找到匹配的異常類型,finally中的代碼都會執(zhí)行(在該方法結(jié)束之前執(zhí)行)
- 如果上層調(diào)用者也不能處理異常,就繼續(xù)向上傳遞
- 一直到main方法也沒有合適的代碼處理異常,就會交給JVM來處理,此時程序異常終止。
3、自定義異常類
具體方法:
- 自定義異常類,然后繼承自Exception或者RunTimeException。
- 實現(xiàn)一個帶有String類型參數(shù)的構(gòu)造方法。
class UserNameException extends Exception { public UserNameException(String message) { super(message); } }
【注】
- 自定義異常通常會繼承自Exception或者RunTimeException。
- 繼承自Exception的異常默認為受查異常。
- 繼承自RunTimeException的異常默認為非受查異常。
到此這篇關(guān)于Java 超詳細講解異常的處理的文章就介紹到這了,更多相關(guān)Java 異常內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java利用Map實現(xiàn)計算文本中字符個數(shù)
這篇文章主要為大家詳細介紹了Java如何利用Map集合實現(xiàn)計算文本中字符個數(shù),文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-08-08Spring Security+JWT實現(xiàn)認證與授權(quán)的實現(xiàn)
本文主要介紹了Spring Security+JWT實現(xiàn)認證與授權(quán)的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04設(shè)計模式之中介者模式_動力節(jié)點Java學(xué)院整理
這篇文章主要為大家詳細介紹了設(shè)計模式之中介者模式的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08