Java異常的處理機(jī)制
圖片解析:
1.生成字節(jié)碼文件的過程可能產(chǎn)生編譯時(shí)異常(checked),由字節(jié)碼文件到在內(nèi)存中加載、運(yùn)行類此過程可能產(chǎn)生運(yùn)行時(shí)異常(unchecked),
2.JAVA程序在執(zhí)行過程中所發(fā)生的異常事件可分為兩類:
> Error: Java虛擬機(jī)無法解決的的嚴(yán)重問題。如:JVM系統(tǒng)內(nèi)部錯(cuò)誤、資源耗盡等嚴(yán)重情況。比如:StackOverflowError和OOM.一般不編寫針對性的代碼進(jìn)行處理。
> Exception: 其他因編程錯(cuò)誤或偶然的外在因素導(dǎo)致的一般性問題,可以使用針對性的代碼進(jì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ò)誤的地方加上檢測的代碼,如進(jìn)行x/y運(yùn)算時(shí),要檢測分母為0,數(shù)據(jù)為空,輸入的不是數(shù)據(jù)而是字符等。過多的if-case分支會(huì)導(dǎo)致程序的代碼加長,臃腫,可讀性差,因此采用異常處理機(jī)制。
Java采用的異常處理機(jī)制,是將異常處理的程序代碼集中在一起,與正常的程序代碼分開,使得程序 簡潔、優(yōu)雅,并易于維護(hù)。
異常的處理:抓拋模型
過程一:"拋":程序在正常執(zhí)行的過程中,一旦出現(xiàn)異常,就會(huì)在異常代碼處生成一個(gè)對應(yīng)異常類的對象,并將此對象拋出。一旦拋出以后,其后的代碼就不再執(zhí)行。
關(guān)于異常對象的產(chǎn)生:A. 系統(tǒng)自動(dòng)生成的異常對象
B. 手動(dòng)的生成一個(gè)異常對象,并拋出(throw)
過程二:"抓":可以理解為異常的處理方式:A.try-catch-finally B.throws
強(qiáng)調(diào):過程一和過程二屬于配合的方式,是并列關(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í)行的代碼 }
說明:
1.finally是可選的(可有,也可沒有,不會(huì)影響異常的處理)
2.使用try將可能出現(xiàn)異常代碼包裝起來,在執(zhí)行過程中,一旦出現(xiàn)異常,就會(huì)生成一個(gè)對應(yīng)異常類的對象,根據(jù)此對象的類型,去catch中進(jìn)行匹配。
3.一旦try中的異常對象匹配到某一個(gè)catch時(shí),就進(jìn)入catch中進(jìn)行異常的處理,一旦處理完成,就跳出當(dāng)前的try-catch結(jié)構(gòu)(在沒有寫finally的情況)。繼續(xù)執(zhí)行其后的代碼。
4.catch中的異常類型如果沒有子父類的關(guān)系,則誰生命在上,誰聲明在下無所謂。
catch中的異常類型如果滿足子父類的關(guān)系,則要求子類一定聲明在父類的上面,否則,報(bào)錯(cuò)
5.常用的異常對象處理的方式:
A.(返回值時(shí)String,可以用輸出語句查看)getMessage()
B.(開發(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:開發(fā)中由于運(yùn)行時(shí)異常比較常見,所以我們通常就不針對運(yùn)行時(shí)異常編寫try-catch-finally了,針對編譯時(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的再說明:
1. finally是可選的
2.finally中聲明的是一定會(huì)被執(zhí)行的代碼,即使catch中又出現(xiàn)異常了、try中有return語句、catch中有return語句等情況。
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語句、catch中有return語句 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語句 //由于方法只能有一個(gè)返回值,所以最后返回的是return 3;
3.像數(shù)據(jù)庫連接、輸入輸出流、網(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è)異常類的對象,此對象滿足throws后異常類型時(shí),就會(huì)被拋出。異常代碼后續(xù)的代碼,就不再執(zhí)行!
2.體會(huì):try-catch-finally:真正的將異常給處理掉了.(以后在其他方法中調(diào)用含有異常的方法時(shí),不會(huì)報(bào)編譯時(shí)錯(cuò)誤了)
throws的方式只是將異常拋給了方法的調(diào)用者。并沒有真正將異常處理掉。(拋至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ī)則之一:
子類重寫的方法拋出的異常類型不大于父類被重寫的方法拋出的異常類型
意味著:如果父類中沒有出現(xiàn)異常,則子類中也不能有異常
我的理解:針對Java中的異常,如果時(shí)編譯時(shí)異常,則需要將其延遲為運(yùn)行時(shí)異常,異常處理機(jī)制的作用也就是這樣,目的是為了給程序員一個(gè)提示,運(yùn)行時(shí)異常的根本還是修改代碼。
手動(dòng)拋出異常對象
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)拋出異常對象 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è)異常對象 } } }
開發(fā)中應(yīng)該如何選擇兩種處理方式?
1. 如果父類中被重寫的方法沒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è)異常類的對象,生成異常對象的過程,聲明在方法體內(nèi)。
throws:屬于異常類處理的一種方式,聲明在方法的聲明處
到此這篇關(guān)于Java異常的處理機(jī)制的文章就介紹到這了,更多相關(guān)Java 異常內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot+rabbitmq實(shí)現(xiàn)指定消費(fèi)者才能消費(fèi)的方法
當(dāng)項(xiàng)目部署到測試環(huán)境后,QA測試過程中,總是“莫名其妙”的發(fā)現(xiàn)所保存的用戶付款單數(shù)據(jù)有問題。這篇文章主要介紹了springboot+rabbitmq實(shí)現(xiàn)指定消費(fèi)者才能消費(fèi),需要的朋友可以參考下2021-11-11Spring Boot2.x集成JPA快速開發(fā)的示例代碼
這篇文章主要介紹了Spring Boot2.x集成JPA快速開發(fā),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05SpringBoot?Test的webEnvironment源碼解讀
這篇文章主要為大家介紹了SpringBoot?Test的webEnvironment源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09java正則表達(dá)式處理花括號內(nèi)容替換賦值問題
這篇文章主要介紹了java正則表達(dá)式處理花括號內(nèi)容替換賦值問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05Java中String.format的使用方法總結(jié)
這篇文章主要介紹了Java中String.format的用法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-03-03java判斷今天,昨天,前天,不能用秒間隔的簡單實(shí)例
下面小編就為大家?guī)硪黄猨ava判斷今天,昨天,前天,不能用秒間隔的簡單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03微服務(wù)中使用Maven BOM來管理你的版本依賴詳解
這篇文章主要介紹了微服務(wù)中使用Maven BOM來管理你的版本依賴,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12