javaSE中異常如何處理舉例詳解
一、異常的初識(shí)
1.1異常的概念
在Java中,將程序執(zhí)行過(guò)程中發(fā)生的不正常行為稱為異常。
在寫代碼時(shí)經(jīng)常遇到的:
1.算數(shù)異常 System.out.println(10 / 0); // 執(zhí)行結(jié)果 Exception in thread "main" java.lang.ArithmeticException: / by zero 2.數(shù)組越界異常 int[] arr = {1, 2, 3}; System.out.println(arr[100]); // 執(zhí)行結(jié)果 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100 3.空指針異常 int[] arr = null; System.out.println(arr.length); // 執(zhí)行結(jié)果 Exception in thread "main" java.lang.NullPointerException
1.2異常的體系結(jié)構(gòu)
異常種類繁多,為了對(duì)不同異?;蛘咤e(cuò)誤進(jìn)行很好的分類管理,Java內(nèi)部維護(hù)了一個(gè)異常的體系結(jié)構(gòu):
從上圖中可以看到:
- Throwable:是異常體系的頂層類,其派生出兩個(gè)重要的子類, Error 和 Exception
- Error:指的是Java虛擬機(jī)無(wú)法解決的嚴(yán)重問(wèn)題,比如:JVM的內(nèi)部錯(cuò)誤、資源耗盡等,典型代表:StackOverflowError和OutOfMemoryError,一旦發(fā)生回力乏術(shù)。
- Exception:異常產(chǎn)生后程序員可以通過(guò)代碼進(jìn)行處理,使程序繼續(xù)執(zhí)行。比如:感冒、發(fā)燒。我們平時(shí)所說(shuō)的異常就是Exception。
1.3異常的分類
異常可能在編譯時(shí)發(fā)生,也可能在程序運(yùn)行時(shí)發(fā)生,根據(jù)發(fā)生的時(shí)機(jī)不同,可以將異常分為:
1.編譯時(shí)異常 在程序編譯期間發(fā)生的異常,稱為編譯時(shí)異常,也稱為受檢查異常(Checked Exception) public class Person { private String name; private String gender; int age; // 想要讓該類支持深拷貝,覆寫Object類的clone方法即可 @Override public Person clone() { return (Person)super.clone(); } } 編譯時(shí)報(bào)錯(cuò): Error:(17, 35) java: 未報(bào)告的異常錯(cuò)誤java.lang.CloneNotSupportedException; 必須對(duì)其進(jìn)行捕獲或聲明以便拋出 2.運(yùn)行時(shí)異常 在程序執(zhí)行期間發(fā)生的異常,稱為運(yùn)行時(shí)異常,也稱為非受檢查異常(Unchecked Exception) RunTimeException以及其子類對(duì)應(yīng)的異常,都稱為運(yùn)行時(shí)異常。比如:NullPointerException、 ArrayIndexOutOfBoundsException、ArithmeticException。 注意:編譯時(shí)出現(xiàn)的語(yǔ)法性錯(cuò)誤,不能稱之為異常。例如將 System.out.println 拼寫錯(cuò)了, 寫成了 system.out.println. 此時(shí)編譯過(guò)程中就會(huì)出錯(cuò), 這是 "編譯期" 出錯(cuò)。而運(yùn)行時(shí)指的是程序已經(jīng)編譯通過(guò)得到class 文件了, 再由 JVM 執(zhí)行過(guò)程中出現(xiàn)的錯(cuò)誤。
二、異常的處理和拋出
2.1防御式編程
錯(cuò)誤在代碼中是客觀存在的. 因此我們要讓程序出現(xiàn)問(wèn)題的時(shí)候及時(shí)通知程序猿. 主要的方式
1. LBYL: Look Before You Leap. 在操作之前就做充分的檢查. 即:事前防御型
boolean ret = false; ret = 登陸游戲(); if (!ret) { 處理登陸游戲錯(cuò)誤; return; } r et = 開(kāi)始匹配(); if (!ret) { 處理匹配錯(cuò)誤; return; } r et = 游戲確認(rèn)(); if (!ret) { 處理游戲確認(rèn)錯(cuò)誤; return; } r et = 選擇英雄(); if (!ret) { 處理選擇英雄錯(cuò)誤; return; } r et = 載入游戲畫(huà)面(); if (!ret) { 處理載入游戲錯(cuò)誤; return; } ...... 缺陷:正常流程和錯(cuò)誤處理流程代碼混在一起, 代碼整體顯的比較混亂。
2. EAFP: It's Easier to Ask Forgiveness than Permission. "事后獲取原諒比事前獲取許可更容易". 也就是先操作, 遇到問(wèn)題再處理. 即:事后認(rèn)錯(cuò)型
try { 登陸游戲(); 開(kāi)始匹配(); 游戲確認(rèn)(); 選擇英雄(); 載入游戲畫(huà)面(); ... } catch (登陸游戲異常) { 處理登陸游戲異常; } catch (開(kāi)始匹配異常) { 處理開(kāi)始匹配異常; } catch (游戲確認(rèn)異常) { 處理游戲確認(rèn)異常; } catch (選擇英雄異常) { 處理選擇英雄異常; } catch (載入游戲畫(huà)面異常) { 處理載入游戲畫(huà)面異常; } ...... 優(yōu)勢(shì):正常流程和錯(cuò)誤流程是分離開(kāi)的, 程序員更關(guān)注正常流程,代碼更清晰,容易理解代碼
異常處理的核心思想就是 EAFP。
在Java中,異常處理主要的5個(gè)關(guān)鍵字:throw、try、catch、final、throws。
2.2異常的拋出
編寫程序時(shí),如果程序中出現(xiàn)錯(cuò)誤,此時(shí)就需要將錯(cuò)誤的信息告知給調(diào)用者,比如:參數(shù)檢測(cè)。
在Java中,可以借助throw關(guān)鍵字,拋出一個(gè)指定的異常對(duì)象,將錯(cuò)誤信息告知給調(diào)用者。具體語(yǔ)法如下:
throw new XXXException("異常產(chǎn)生的原因");
舉個(gè)例子:實(shí)現(xiàn)一個(gè)獲取數(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ù)組下標(biāo)越界"); } r eturn array[index]; } public static void main(String[] args) { int[] array = {1,2,3}; getElement(array, 3); }
【注意事項(xiàng)】
1. throw必須寫在方法體內(nèi)部
2. 拋出的對(duì)象必須是Exception 或者 Exception 的子類對(duì)象
3. 如果拋出的是 RunTimeException 或者 RunTimeException 的子類,則可以不用處理,直接交給JVM來(lái)處理
4. 如果拋出的是編譯時(shí)異常,用戶必須處理,否則無(wú)法通過(guò)編譯
5. 異常一旦拋出,其后的代碼就不會(huì)執(zhí)行
2.3異常的捕獲
異常的捕獲,也就是異常的具體處理方式,主要有兩種:異常聲明throws 以及 try-catch捕獲處理。
異常聲明throws:
處在方法聲明時(shí)參數(shù)列表之后,當(dāng)方法中拋出編譯時(shí)異常,用戶不想處理該異常,此時(shí)就可以借助throws將異常拋給方法的調(diào)用者來(lái)處理。即當(dāng)前方法不處理異常,提醒方法的調(diào)用者處理異常。
語(yǔ)法格式:
修飾符 返回值類型 方法名(參數(shù)列表) throws 異常類型1,異常類型2...{
}
舉例:加載指定的配置文件config.ini
public class Config { File file; /* FileNotFoundException : 編譯時(shí)異常,表明文件不存在 此處不處理,也沒(méi)有能力處理,應(yīng)該將錯(cuò)誤信息報(bào)告給調(diào)用者,讓調(diào)用者檢查文件名字是否給錯(cuò)誤了 */ public void OpenConfig(String filename) throws FileNotFoundException{ if(filename.equals("config.ini")){ throw new FileNotFoundException("配置文件名字不對(duì)"); } / / 打開(kāi)文件 } public void readConfig(){ } }
【注意事項(xiàng)】
1. throws必須跟在方法的參數(shù)列表之后
2. 聲明的異常必須是 Exception 或者 Exception 的子類
3. 方法內(nèi)部如果拋出了多個(gè)異常,throws之后必須跟多個(gè)異常類型,之間用逗號(hào)隔開(kāi),如果拋出多個(gè)異常類型具有父子關(guān)系,直接聲明父類即可。4.調(diào)用聲明拋出異常的方法時(shí),調(diào)用者必須對(duì)該異常進(jìn)行處理,或者繼續(xù)使用throws拋出
try-catch捕獲并處理:
throws對(duì)異常并沒(méi)有真正處理,而是將異常報(bào)告給拋出異常方法的調(diào)用者,由調(diào)用者處理。如果真正要對(duì)異常進(jìn)行處理,就需要try-catch。
語(yǔ)法格式: try{ // 將可能出現(xiàn)異常的代碼放在這里 }catch(要捕獲的異常類型 e){ // 如果try中的代碼拋出異常了,此處catch捕獲時(shí)異常類型與try中拋出的異常類型一致時(shí),或者是try中拋出異常的基類 時(shí),就會(huì)被捕獲到 // 對(duì)異常就可以正常處理,處理完成后,跳出try-catch結(jié)構(gòu),繼續(xù)執(zhí)行后序代碼 }[catch(異常類型 e){ // 對(duì)異常進(jìn)行處理 }finally{ // 此處代碼一定會(huì)被執(zhí)行到 }] // 后序代碼 // 當(dāng)異常被捕獲到時(shí),異常就被處理了,這里的后序代碼一定會(huì)執(zhí)行 // 如果捕獲了,由于捕獲時(shí)類型不對(duì),那就沒(méi)有捕獲到,這里的代碼就不會(huì)被執(zhí)行 注意: 1. []中表示可選項(xiàng),可以添加,也可以不用添加 2. try中的代碼可能會(huì)拋出異常,也可能不會(huì)
【注意事項(xiàng)】
1. try塊內(nèi)拋出異常位置之后的代碼將不會(huì)被執(zhí)行
2. 如果拋出異常類型與catch時(shí)異常類型不匹配,即異常不會(huì)被成功捕獲,也就不會(huì)被處理,繼續(xù)往外拋,直到JVM收到后中斷程序----異常是按照類型來(lái)捕獲的
2.4異常的處理流程
關(guān)于 "調(diào)用棧"
方法之間是存在相互調(diào)用關(guān)系的, 這種調(diào)用關(guān)系我們可以用 "調(diào)用棧" 來(lái)描述. 在 JVM 中有一塊內(nèi)存空間稱為"虛擬機(jī)棧" 專門存儲(chǔ)方法之間的調(diào)用關(guān)系. 當(dāng)代碼中出現(xiàn)異常的時(shí)候, 我們就可以使用 e.printStackTrace(); 的方式查看出現(xiàn)異常代碼的調(diào)用棧.舉例:
public static void main(String[] args) { try { func(); } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); } System.out.println("after try catch"); } public static void func() { int[] arr = {1, 2, 3}; System.out.println(arr[100]); } // 直接結(jié)果 java.lang.ArrayIndexOutOfBoundsException: 100 at demo02.Test.func(Test.java:18) at demo02.Test.main(Test.java:9) after try catch
【異常處理流程總結(jié)】
- 程序先執(zhí)行 try 中的代碼
- 如果 try 中的代碼出現(xiàn)異常, 就會(huì)結(jié)束 try 中的代碼, 看和 catch 中的異常類型是否匹配.
- 如果找到匹配的異常類型, 就會(huì)執(zhí)行 catch 中的代碼
- 如果沒(méi)有找到匹配的異常類型, 就會(huì)將異常向上傳遞到上層調(diào)用者.
- 無(wú)論是否找到匹配的異常類型, finally 中的代碼都會(huì)被執(zhí)行到(在該方法結(jié)束之前執(zhí)行).
- 如果上層調(diào)用者也沒(méi)有處理的了異常, 就繼續(xù)向上傳遞.
- 一直到 main 方法也沒(méi)有合適的代碼處理異常, 就會(huì)交給 JVM 來(lái)進(jìn)行處理, 此時(shí)程序就會(huì)異常終止.
三、自定義異常類
3.1舉例:實(shí)現(xiàn)一個(gè)用戶登錄功能
public class LogIn { private String userName = "admin"; private String password = "123456"; public static void loginInfo(String userName, String password) { if (!userName.equals(userName)) { } if(!password.equals(password)) { } System.out.println("登陸成功"); } public static void main(String[] args) { loginInfo("admin", "123456"); } } 具體方式: 1. 自定義異常類,然后繼承自Exception 或者 RunTimeException 2. 實(shí)現(xiàn)一個(gè)帶有String類型參數(shù)的構(gòu)造方法,參數(shù)含義:出現(xiàn)異常的原因
class UserNameException extends Exception { public UserNameException(String message) { super(message); } } class PasswordException extends Exception { public PasswordException(String message) { super(message); } }
此時(shí)我們的 login 代碼可以改成:
public class LogIn { private String userName = "admin"; private String password = "123456"; public static void loginInfo(String userName, String password) throws UserNameException,PasswordException{ if (!userName.equals(userName)) { throw new UserNameException("用戶名錯(cuò)誤!"); } if(!password.equals(password)) { throw new PasswordException("用戶名錯(cuò)誤!"); } System.out.println("登陸成功"); } public static void main(String[] args) { try { loginInfo("admin", "123456"); } catch (UserNameException e) { e.printStackTrace(); } catch (PasswordException e) { e.printStackTrace(); } } }
注意事項(xiàng)
自定義異常通常會(huì)繼承自 Exception 或者 RuntimeException
繼承自 Exception 的異常默認(rèn)是受查異常
繼承自 RuntimeException 的異常默認(rèn)是非受查異常
總結(jié)
到此這篇關(guān)于javaSE中異常如何處理的文章就介紹到這了,更多相關(guān)javaSE異常處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis Plus 多表聯(lián)合查詢的實(shí)現(xiàn)示例
這篇文章主要介紹了mybatis Plus 多表聯(lián)合查詢的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09詳解SpringMVC的攔截器鏈實(shí)現(xiàn)及攔截器鏈配置
攔截器(Interceptor)是一種動(dòng)態(tài)攔截方法調(diào)用的機(jī)制,在SpringMVC中動(dòng)態(tài)攔截控制器方法的執(zhí)行。本文將詳細(xì)講講SpringMVC中攔截器參數(shù)及攔截器鏈配置,感興趣的可以嘗試一下2022-08-08詳解Spring簡(jiǎn)單容器中的Bean基本加載過(guò)程
本篇將對(duì)定義在 XMl 文件中的 bean,從靜態(tài)的的定義到變成可以使用的對(duì)象的過(guò)程,即 bean 的加載和獲取的過(guò)程進(jìn)行一個(gè)整體的了解2017-05-05Java Date類常用示例_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
在JDK1.0中,Date類是唯一的一個(gè)代表時(shí)間的類,但是由于Date類不便于實(shí)現(xiàn)國(guó)際化,所以從JDK1.1版本開(kāi)始,推薦使用Calendar類進(jìn)行時(shí)間和日期處理。這里簡(jiǎn)單介紹一下Date類的使用,需要的朋友可以參考下2017-05-05Java讀取制表符文本轉(zhuǎn)換為JSON實(shí)現(xiàn)實(shí)例
在Java開(kāi)發(fā)中,處理各種數(shù)據(jù)格式是常見(jiàn)的任務(wù),本文將介紹如何使用Java讀取制表符文本文件,并將其轉(zhuǎn)換為JSON格式,以便于后續(xù)的數(shù)據(jù)處理和分析,我們將使用Java中的相關(guān)庫(kù)來(lái)實(shí)現(xiàn)這個(gè)過(guò)程,并提供詳細(xì)的代碼示例2024-01-01Mybatis數(shù)據(jù)批量插入如何實(shí)現(xiàn)
這篇文章主要介紹了Mybatis數(shù)據(jù)批量插入如何實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07