Java異常處理深入理解
圖片解析:
1.生成字節(jié)碼文件的過(guò)程可能產(chǎn)生編譯時(shí)異常(checked),由字節(jié)碼文件到在內(nèi)存中加載、運(yùn)行類此過(guò)程可能產(chǎn)生運(yùn)行時(shí)異常(unchecked),
2.JAVA程序在執(zhí)行過(guò)程中所發(fā)生的異常事件可分為兩類:
> Error: Java虛擬機(jī)無(wú)法解決的的嚴(yán)重問(wèn)題。如:JVM系統(tǒng)內(nèi)部錯(cuò)誤、資源耗盡等嚴(yán)重情況。比如:StackOverflowError和OOM.一般不編寫針對(duì)性的代碼進(jìn)行處理。
> Exception: 其他因編程錯(cuò)誤或偶然的外在因素導(dǎo)致的一般性問(wèn)題,可以使用針對(duì)性的代碼進(jìn)行處理。例如:
空指針訪問(wèn)、試圖讀取不存在的文件、網(wǎng)絡(luò)連接中斷、數(shù)組角標(biāo)越界
捕獲錯(cuò)誤最理想的實(shí)在編譯期間,但有的錯(cuò)誤只有在運(yùn)行時(shí)才會(huì)發(fā)生。比如:除數(shù)為0,數(shù)組角標(biāo)越界等
編譯時(shí)異常(執(zhí)行javac.exe命令時(shí),可能出現(xiàn)的異常):IOException
、FileNotFoundException(IOException的子類)
、ClassNotFoundException
運(yùn)行時(shí)異常(執(zhí)行java.exe命令時(shí),出現(xiàn)的異常):NullPointerException
、ArrayIndexOutOfBoundsException
、ArrayIndexOutOfBoundsException
、ClassCastException
、NumberFormatException
、InputMismatchException
、ArithmeticException
public class ExceptionTest{ //NullPointerException @Test public void test1(){ int[] arr = null; System.out.println(arr[3]); str = null; System.out.println(str.charAt(0)); } //IndexOutOfBoundsException @Test public void test2(){ //ArrayIndexOutOfBoundsException int[] arr = new int[10]; System.out.println(arr[10]); //StringIndexOutOfBoundsException str = "abc"; System.out.println(str.charAt(3)); } //ClassCastException:類型不匹配 @Test public void test3(){ Object obj = new Date(); String str = (String)obj; } //NumberFormatException:數(shù)據(jù)格式異常 @Test public void test4(){ str = "abc"; int num = Integer.parseInt(str); } //InputMismatchException:輸入不匹配異常 @Test public void test5(){ Scanner scanner = new Scanner(System.in); int score = scanner.nextInt(); System.out.println(score); } //ArithmeticException:算數(shù)異常 @Test public void test6(){ int a = 10; int b= 0; System.out.println(a\b); } }
異常的處理:
在編寫程序時(shí),經(jīng)常要在可能出現(xiàn)錯(cuò)誤的地方加上檢測(cè)的代碼,如進(jìn)行x/y運(yùn)算時(shí),要檢測(cè)分母為0,數(shù)據(jù)為空,輸入的不是數(shù)據(jù)而是字符等。過(guò)多的if-case分支會(huì)導(dǎo)致程序的代碼加長(zhǎng),臃腫,可讀性差,因此采用異常處理機(jī)制。
Java采用的異常處理機(jī)制,是將異常處理的程序代碼集中在一起,與正常的程序代碼分開(kāi),使得程序 簡(jiǎn)潔、優(yōu)雅,并易于維護(hù)。
異常的處理:抓拋模型
過(guò)程一:"拋":程序在正常執(zhí)行的過(guò)程中,一旦出現(xiàn)異常,就會(huì)在異常代碼處生成一個(gè)對(duì)應(yīng)異常類的對(duì)象,并將此對(duì)象拋出。一旦拋出以后,其后的代碼就不再執(zhí)行。
關(guān)于異常對(duì)象的產(chǎn)生:
A. 系統(tǒng)自動(dòng)生成的異常對(duì)象
B. 手動(dòng)的生成一個(gè)異常對(duì)象,并拋出(throw)
過(guò)程二:"抓":可以理解為異常的處理方式:A.try-catch-finally B.throws
強(qiáng)調(diào):過(guò)程一和過(guò)程二屬于配合的方式,是并列關(guān)系
處理機(jī)制一:try-catch-finally
try{ //可能出現(xiàn)異常的代碼 }catch(異常類型1 變量名1){//一段代碼可能有多個(gè)異常類型 //處理異常的方式1 }catch(異常類型2 變量名2){ //處理異常的方式2 }catch(異常類型3 變量名3){ //處理異常的方式3 } ..... finally{ //一定會(huì)執(zhí)行的代碼 }
說(shuō)明:
1.finally是可選的(可有,也可沒(méi)有,不會(huì)影響異常的處理)
2.使用try將可能出現(xiàn)異常代碼包裝起來(lái),在執(zhí)行過(guò)程中,一旦出現(xiàn)異常,就會(huì)生成一個(gè)對(duì)應(yīng)異常類的對(duì)象,根據(jù)此對(duì)象的類型,去catch中進(jìn)行匹配。
3.一旦try中的異常對(duì)象匹配到某一個(gè)catch時(shí),就進(jìn)入catch中進(jìn)行異常的處理,一旦處理完成,就跳出當(dāng)前的try-catch結(jié)構(gòu)(在沒(méi)有寫finally的情況)。繼續(xù)執(zhí)行其后的代碼。
4.
catch中的異常類型如果沒(méi)有子父類的關(guān)系,則誰(shuí)生命在上,誰(shuí)聲明在下無(wú)所謂。
catch中的異常類型如果滿足子父類的關(guān)系,則要求子類一定聲明在父類的上面,否則,報(bào)錯(cuò)
5.常用的異常對(duì)象處理的方式:
A.(返回值時(shí)String,可以用輸出語(yǔ)句查看)getMessage()
B.(開(kāi)發(fā)中常用)printStackTrace()
6.在try結(jié)構(gòu)中聲明的變量:再出了try結(jié)構(gòu)以后,就不能在被調(diào)用
7.try-catch-finally結(jié)構(gòu)可以嵌套
體會(huì)1:使用try-catch-finally處理編譯時(shí)異常,使得程序在編譯時(shí)就不再報(bào)錯(cuò),但是運(yùn)行時(shí)仍可能報(bào)錯(cuò)。相當(dāng)于我們使用try-catch-finally將一個(gè)編譯時(shí)可能出現(xiàn)的異常,延遲到運(yùn)行時(shí)出現(xiàn)(即把編譯時(shí)異常轉(zhuǎn)換為運(yùn)行時(shí)異常)
體會(huì)2:開(kāi)發(fā)中由于運(yùn)行時(shí)異常比較常見(jiàn),所以我們通常就不針對(duì)運(yùn)行時(shí)異常編寫try-catch-finally了,針對(duì)編譯時(shí)異常,我們一定要考慮異常的處理。
@Test public void test1() { str = "abc"; int num = 0;// 聲明放在外面,出了try,還可以使用num; //但是此時(shí)要注意:在try-catch結(jié)構(gòu)中,num不一定被賦值,所以要手動(dòng)的給賦默認(rèn)初始化值 try { num = Integer.parseInt(str); } catch (NullPointerException e) { System.out.println(e.getMessage()); } catch (NumberFormatException e) { e.printStackTrace(); } System.out.println(num); }
finally的再說(shuō)明:
1. finally是可選的
2.finally中聲明的是一定會(huì)被執(zhí)行的代碼,即使catch中又出現(xiàn)異常了、try中有return語(yǔ)句、catch中有return語(yǔ)句等情況。
3.finally中一定會(huì)執(zhí)行的結(jié)構(gòu)在加載順序上優(yōu)于try、catch中的異常代碼
@Test//catch中又出現(xiàn)異常了 public void test1(){ try{ int a = 10; int b = 0; System.out.println(a / b); }catch(ArithmeticException e){ int[] arr = new int[10]; System.out.println(arr[10]); } //System.out.println("catch中又出現(xiàn)異常了,但我一定不會(huì)輸出!"); finally{ System.out.println("catch中又出現(xiàn)異常了,但我一定會(huì)輸出!"); } }
@Test//try中有return語(yǔ)句、catch中有return語(yǔ)句 public void testMethod(){ int num = method(); } public int method(){ try{ int[] arr = new int[10]; System.out.println("arr[10]"); return 1; }catch(ArrayIndexOutOfBoundException e){ e.printStockTrace(); return 2; }finally{ System.out.println("我一定會(huì)被執(zhí)行!"); return 3; } } //finally中一定會(huì)執(zhí)行的結(jié)構(gòu)在執(zhí)行順序上優(yōu)于try、catch中的return語(yǔ)句 //由于方法只能有一個(gè)返回值,所以最后返回的是return 3;
4.像數(shù)據(jù)庫(kù)連接、輸入輸出流、網(wǎng)絡(luò)編程Socket等資源,JVM是不能自動(dòng)的回收的,我們需要自己手動(dòng)的進(jìn)行資源的釋放。此時(shí),就需要聲明在finally中。
處理機(jī)制二:throws + 異常類型
1. "throws + 異常類型"寫在方法的聲明處,指明此方法執(zhí)行時(shí),可能會(huì)拋出的異常類型。
一旦當(dāng)方法體執(zhí)行時(shí),出現(xiàn)異常,仍會(huì)在異常代碼處生成一個(gè)異常類的對(duì)象,此對(duì)象滿足throws后異常類型時(shí),就會(huì)被拋出。異常代碼后續(xù)的代碼,就不再執(zhí)行!
2.體會(huì):
- try-catch-finally:真正的將異常給處理掉了.(以后在其他方法中調(diào)用含有異常的方法時(shí),不會(huì)報(bào)編譯時(shí)錯(cuò)誤了)
- throws的方式只是將異常拋給了方法的調(diào)用者。并沒(méi)有真正將異常處理掉。(拋至main時(shí),必須處理掉,不然拋給JVM,JVM就掛了)
public class ExceptionTest2 { public static void main(String[] args) { try { method2(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } method3();// 在method2()里已經(jīng)解決了異常,所以可以調(diào)用 } public static void method3() { try { method2(); } catch (IOException e) { e.printStackTrace(); } } public static void method2() throws FileNotFoundException, IOException { method1(); } public static void method1() throws FileNotFoundException, IOException {//異常代碼 File file = new File("hello.txt"); FileInputStream fis = new FileInputStream(file); int data = fis.read(); while (data != -1) { System.out.print((char) data); data = fis.read(); } fis.close(); System.out.println("hanhan");// 不會(huì)被執(zhí)行! } }
方法重寫的規(guī)則之一:
子類重寫的方法拋出的異常類型不大于父類被重寫的方法拋出的異常類型意味著:如果父類中沒(méi)有出現(xiàn)異常,則子類中也不能有異常
我的理解:針對(duì)Java中的異常,如果時(shí)編譯時(shí)異常,則需要將其延遲為運(yùn)行時(shí)異常,異常處理機(jī)制的作用也就是這樣,目的是為了給程序員一個(gè)提示,運(yùn)行時(shí)異常的根本還是修改代碼。
手動(dòng)拋出異常對(duì)象
public class StudentTest { public static void main(String[] args) { Student s = new Student(); s.regist(-1001); System.out.println(s); } } class Student { private int id; public void regist(int id) { if (id > 0) { this.id = id; } else {//方式一:拋出運(yùn)行時(shí)異常,運(yùn)行時(shí)會(huì)報(bào)錯(cuò) // 手動(dòng)拋出異常對(duì)象 throw new RuntimeException("您輸入的數(shù)據(jù)非法!"); } } }
public class StudentTest { public static void main(String[] args) { try { Student s = new Student(); s.regist(-1001); System.out.println(s); } catch (Exception e) { System.out.println(e.getMessage()); } } } class Student { private int id; public void regist(int id) throws Exception {//throws:體現(xiàn)異常的處理 if (id > 0) { this.id = id; } else {//拋出編譯時(shí)異常:必須顯式的處理 throw new Exception("您輸入的數(shù)據(jù)非法!");//throw:體現(xiàn)生成一個(gè)異常對(duì)象 } } }
開(kāi)發(fā)中應(yīng)該如何選擇兩種處理方式?
1. 如果父類中被重寫的方法沒(méi)throws方式處理異常,則子類重寫的方法也不能使用throws,意味著如果子類重寫的方法中異常,必須使用try-catch-finally方式處理。
2.執(zhí)行的方法a中,先后有調(diào)用了另外的幾個(gè)方法,這幾個(gè)方法時(shí)遞進(jìn)關(guān)系執(zhí)行的,我們建議這幾個(gè)方法使用throws的方式進(jìn)行處理,而執(zhí)行的方法a可以考慮使用try-catch-finally方式進(jìn)行處理。 用戶自定義異常類
如何自定義異常類?
1.繼承于現(xiàn)有的異常結(jié)構(gòu):RuntimeException、Exception
2.提供全局變量:serialVersionUID
3.提供重載的構(gòu)造器
public class StudentTest { public static void main(String[] args) { try { Student s = new Student(); s.regist(-1001); System.out.println(s); } catch (Exception e) { System.out.println(e.getMessage()); } } } class Student { private int id; public void regist(int id) throws Exception { if (id > 0) { this.id = id; } else { throw new MyException("不能輸入負(fù)數(shù)");//自定義異常類的使用 } } } public class MyException extends RuntimeException {//自定義異常類 static final long serialVersionUID = -7034897190745766939L; public MyException() { } public MyException(String msg) { super(msg); } }
throw和throws的區(qū)別:
throw:表示拋出一個(gè)異常類的對(duì)象,生成異常對(duì)象的過(guò)程,聲明在方法體內(nèi)。
throws:屬于異常類處理的一種方式,聲明在方法的聲明處
總結(jié)
本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Java實(shí)現(xiàn)簡(jiǎn)易學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)易學(xué)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07spring-gateway網(wǎng)關(guān)聚合swagger實(shí)現(xiàn)多個(gè)服務(wù)接口切換的示例代碼
這篇文章主要介紹了spring-gateway網(wǎng)關(guān)聚合swagger實(shí)現(xiàn)多個(gè)服務(wù)接口切換的示例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03Java中實(shí)現(xiàn)多重排序的幾種方法小結(jié)
Java中的多重排序通常指的是同時(shí)對(duì)一個(gè)集合中的兩個(gè)或更多列或多維度的數(shù)據(jù)進(jìn)行排序,這通常通過(guò)自定義Comparator實(shí)現(xiàn),可以結(jié)合Arrays.sort()或Collections.sort()方法,當(dāng)需要進(jìn)行多重排序時(shí),即根據(jù)多個(gè)字段進(jìn)行排序,我們可以采用以下幾種方法2024-10-10Java transient關(guān)鍵字與序列化操作實(shí)例詳解
這篇文章主要介紹了Java transient關(guān)鍵字與序列化操作,結(jié)合實(shí)例形式詳細(xì)分析了java序列化操作相關(guān)實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參考下2019-09-09Idea導(dǎo)入eureka源碼實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Idea導(dǎo)入eureka源碼實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08Java 如何使用Feign發(fā)送HTTP請(qǐng)求
這篇文章主要介紹了Java 如何使用Feign發(fā)送HTTP請(qǐng)求,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下2020-11-11詳解Java的Hibernate框架中的搜索工具的運(yùn)用
這篇文章主要介紹了詳解Java的Hibernate框架中的搜索工具的運(yùn)用,Hibernate是Java的SSH三大web開(kāi)發(fā)框架之一,需要的朋友可以參考下2015-11-11