摘要:異常處理機(jī)制異常與異常類的繼承體系在程序中,當(dāng)程序出現(xiàn)意外情況時(shí),系統(tǒng)會(huì)自動(dòng)生成一個(gè)來(lái)通知程序,從異常發(fā)生出逐漸向外傳播,如果沒(méi)有人來(lái)處理該異常,就會(huì)交給來(lái)處理,對(duì)異常的處理方法是,打印異常跟蹤棧信息,并中止程序的執(zhí)行。
1 為什么要處理異常?
異常機(jī)制可以使程序的異常處理代碼與正常業(yè)務(wù)代碼分離,保證程序代碼的健壯性。
在設(shè)計(jì)程序的時(shí)候,好的程序需要盡可能處理已知的可能產(chǎn)生的錯(cuò)誤,但是事實(shí)上并不可能考慮到所有可能產(chǎn)生異常的情況,同時(shí),眾多的類似if-else的錯(cuò)誤處理代碼可能會(huì)帶來(lái)很多的代碼冗雜,錯(cuò)誤處理與業(yè)務(wù)代碼混雜的情況,帶來(lái)閱讀和維護(hù)的難度。因此我們需要合適的異常處理機(jī)制。
java的異常機(jī)制依靠于try,catch,finally,throw,throws關(guān)鍵字,其中try塊中通常放置可能引發(fā)異常的代碼,catch后對(duì)應(yīng)異常類型和響應(yīng)的異常處理代碼塊,finally塊在java異常機(jī)制中總是會(huì)被執(zhí)行,通常用于回收try塊中打開(kāi)的物理資源,throw用于拋出一個(gè)實(shí)際的異常(異常的實(shí)例),throws主要在方法簽名中使用,用于聲明該方法可能會(huì)拋出的異常,方便或者提醒方法的使用者來(lái)捕獲并處理異常。
2 異常處理機(jī)制
2.1 異常與異常類的繼承體系
在java程序中,當(dāng)程序出現(xiàn)意外情況時(shí),系統(tǒng)會(huì)自動(dòng)生成一個(gè)Exception來(lái)通知程序,從異常發(fā)生出逐漸向外傳播,如果沒(méi)有人來(lái)處理該異常,就會(huì)交給jvm來(lái)處理,jvm對(duì)異常的處理方法是,打印異常跟蹤棧信息,并中止程序的執(zhí)行。
java提供了豐富的異常類體系,所有的異常類都繼承自Throwable父類
java異常處理體系.jpg
注意:
Error錯(cuò)誤:一般與虛擬機(jī)相關(guān),如系統(tǒng)崩潰,動(dòng)態(tài)鏈接錯(cuò)誤等,這種錯(cuò)誤無(wú)法恢復(fù)也無(wú)法捕獲,所以應(yīng)用程序一般不能處理這些錯(cuò)誤,也不應(yīng)該試圖去捕獲這類對(duì)象
我們主要處理的Exception類,該類在java中分為兩類,一種是Checked異常,一種是Runtime異常。Checked異常是java特有的,在java設(shè)計(jì)哲學(xué)中,Checked異常被認(rèn)為是可以被處理或者修復(fù)的異常,所以java程序必須顯式處理Checked異常,當(dāng)我們使用或者出現(xiàn)Checked異常類的時(shí)候,程序中要么顯式try- catch捕獲該異常并修復(fù),要么顯式聲明拋出該異常,否則程序無(wú)法通過(guò)編譯。(Checked異常某種程度上降低了開(kāi)發(fā)生產(chǎn)率和代碼執(zhí)行率,在java領(lǐng)域是一個(gè)備受爭(zhēng)論的問(wèn)題)
常見(jiàn)的運(yùn)行時(shí)異常:如上圖所示,這里列舉幾個(gè)比較常見(jiàn)的。
public class test{ public static void main(String[] args){ try{ int a = Integer.parseInt(args[0]); int b = Integer.parseInt(args[1)); int c = a/b; System.out.println("您輸出的結(jié)果是"+c); } catch(IndexOutOfBoundsException ie){ System.out.println("數(shù)組越界,輸入的參數(shù)不夠"); } catch(NumberFormatException ne){ System.out.println("數(shù)字格式異常:程序只能接收整數(shù)參數(shù)"); } catch(ArithmeticException ae){ System.out.println("算術(shù)法異常"); } catch(Exception e){ System.out.println("出現(xiàn)異常"); } Date d = null; try{ System.out.println(d.after(new Date())); } // 試圖調(diào)用一個(gè)null對(duì)象的方法或者實(shí)例變量時(shí) 出現(xiàn)的異常 catch(NullPointerException ne){ System.out.println("指向異常"); } } }
程序中一般將Exception放在最后,先捕獲小異常(子類異常),再捕獲大異常。如果順序顛倒,還會(huì)出現(xiàn)編譯錯(cuò)誤。
2.2 try ... catch異常捕獲
try ... catch 是最常見(jiàn)的異常捕獲語(yǔ)句,try后與{ }配對(duì)使用,其中是業(yè)務(wù)實(shí)現(xiàn)代碼,如果try塊中出現(xiàn)問(wèn)題或者異常,系統(tǒng)自動(dòng)生成一個(gè)異常對(duì)象,該異常對(duì)象提交到Java運(yùn)行環(huán)境,java運(yùn)行環(huán)境收到異常對(duì)象后,會(huì)尋找能夠處理該異常的catch塊,如果找到,就將異常對(duì)象交給該catch塊處理,如果沒(méi)有找到,就終止程序運(yùn)行。
從異常捕獲過(guò)程中,我們可以看到程序要慎重考慮可能出現(xiàn)異常的代碼塊,否則這段程序是不夠健壯的,一個(gè)經(jīng)常出現(xiàn)崩潰或被終止的程序,是災(zāi)難性的。
catch塊中如何處理異常:
一個(gè)try塊之后可能存在多個(gè)catch塊,java運(yùn)行時(shí)與catch塊()內(nèi)的異常類進(jìn)行比較,判斷該異常是否 instanceof 該類,如果屬于該類,就將該異常對(duì)象傳給catch塊內(nèi)的異常形參,catch塊后可以對(duì)該異常進(jìn)行處理,獲取相關(guān)異常的詳細(xì)信息等。注意系統(tǒng)生成的異常實(shí)例對(duì)象是相對(duì)具體的子類異常對(duì)象,而進(jìn)入一個(gè)catch塊后就不會(huì)再進(jìn)入下一個(gè)catch塊,所以這也是我們盡量將小異常放在大異常前面的原因。
常見(jiàn)操作:輸出出現(xiàn)異常提示信息
訪問(wèn)異常信息,打印異常信息
getMessage();// 返回詳細(xì)描述字符串 printStackTrace();// 打印異常跟蹤棧信息到標(biāo)準(zhǔn)錯(cuò)誤窗口 printStackTrace(PrintStream s);// 跟蹤棧信息輸出到指定輸出流 getStackTrace();// 返回跟蹤棧信息
采用別的替選數(shù)據(jù)或方案,或者提示用戶重新操作,或者重新拋出異常,進(jìn)行異常轉(zhuǎn)譯,重新包裝,交給上層調(diào)用者來(lái)對(duì)該異常進(jìn)行處理。
多異常捕獲:(java 7 提供的新特性)
public class test{ public static void main(String[] args){ try{ int a = Integer.parseInt(args[0]); int b = Integer.parseInt(args[1)); int c = a/b; System.out.println("您輸出的結(jié)果是"+c); } catch(IndexOutOfBoundsException ie | NumberFormatException ne |ArithmeticException ae){ System.out.println("程序發(fā)生上述異常的某一種"); // 此時(shí)ie ,ne, ae都是默認(rèn) final修飾的變量 不能再重新賦值 } catch(Exception e){ System.out.println("出現(xiàn)異常"); // 此時(shí)可以賦值 e = new RuntimeException("new error"); } } }
2.3 finally 一定會(huì)執(zhí)行的finally模塊
通常try塊里打開(kāi)了一些物理資源,(比如磁盤(pán)文件,數(shù)據(jù)庫(kù)連接等),這些需要保證回收,所以我們通常在finally塊中進(jìn)行回收。
舉個(gè)例子
public class test{ public static void main(String[] args){ FileInputStream fis = null; try{ fis = new FileInputStream("a.txt"); } catch(IOException e){ System.out.println(e.getMessage); //如果執(zhí)行return,程序會(huì)先跳到finally塊執(zhí)行,執(zhí)行完之后再回來(lái)執(zhí)行return語(yǔ)句 return; // 如果這里是System.exit(0),因?yàn)槭峭顺鎏摂M機(jī) // 所以finally塊沒(méi)辦法執(zhí)行 } finally{ if(fis!= null){ try{ fis.close(); } catch(IOException e){ e.printStackTrace(); } } } } }
注意:盡量不要在finally塊中使用return或者throw語(yǔ)句,因?yàn)橐坏┰趂inally中執(zhí)行,程序就不會(huì)再跳回原來(lái)try或者catch塊中執(zhí)行原本應(yīng)該執(zhí)行的return和throw語(yǔ)句了,程序自己就結(jié)束了,這可能會(huì)帶來(lái)一些麻煩的錯(cuò)誤。
為了方便物理資源的關(guān)閉,java7 提供了一種新的語(yǔ)法,增強(qiáng)了try的功能,可以在try后面跟一對(duì)圓括號(hào),來(lái)聲明初始化物理資源,try執(zhí)行完畢后會(huì)自動(dòng)關(guān)閉資源。
public class AutoClose{ public static void main(String[] args) throws IOException { // ()內(nèi)的資源類必須實(shí)現(xiàn)AutoCloseable 或者Closeable接口中的close()方法 try(BufferedReader br = new BufferedReader( new FileReader("auto.java"))) { System.out.println(br.readLine()); } } }
2.4 throws 關(guān)鍵字:聲明拋出異常
throws聲明拋出異常,在方法簽名中使用,上面的AtuoClose就是其使用的例子。它可以聲明拋出多個(gè)類,多個(gè)類之間用“,”隔開(kāi)。
首先要理解我們?yōu)槭裁匆暶鲯伋霎惓#寒?dāng)某個(gè)方法中程序的執(zhí)行可能會(huì)出現(xiàn)異常,但是該方法并不知道如何處理異常,或者我們想把這個(gè)異常交給上層方法調(diào)用者來(lái)處理或者修復(fù),那我們給該方法加上關(guān)鍵字throws 異常,以聲明該方法可能會(huì)出現(xiàn)的異常。自然,加了throws關(guān)鍵字之后,該方法我們就無(wú)需再用try—catch來(lái)捕獲異常了,因?yàn)檫@已經(jīng)不是我們這個(gè)方法需要操心的事情了。
注意使用throws聲明異常的時(shí)候,涉及到子類對(duì)父類的方法重寫(xiě)時(shí),子類聲明的異常類型應(yīng)該是父類方法聲明的異常類型的子類或者相同類。
如果throws 聲明的是checked異常,根據(jù)checked異常的規(guī)定,我們不能對(duì)該異常視而不見(jiàn),因?yàn)槲覀儽仨毺幚碓摦惓#援?dāng)拿到一個(gè)聲明了可能會(huì)發(fā)生checked異常的方法時(shí),在調(diào)用該方法時(shí),要么放在try塊中來(lái)顯式捕捉該異常,要么放在另外一個(gè)帶throws聲明異常的方法中。
所以使用checked異常時(shí),要特別注意處理它的問(wèn)題,還會(huì)帶來(lái)方法重寫(xiě)的限制性,因此大部分時(shí)候推薦使用Runtime異常。
public class ThrowTest{ public static void main(String[] args) throws Exception { test();//test 聲明會(huì)產(chǎn)生checked 異常 因此main函數(shù)也需要聲明異常 // 或者在try - catch 中捕獲該異常 } public void test() throws IOException{ FileInputStream fis = new FileInputStream("a.text"); } }
2.5 throw關(guān)鍵字 :拋出異常
java允許程序自行拋出異常,通常系統(tǒng)幫助我們檢查是否發(fā)生一些普遍定義的異常,但是有些異常可能不是普遍定義的,只是與我們業(yè)務(wù)不符,所以我們可以自行拋出異常,也可以自行拋出一些我們自定義的異常,而拋出異常的行為與系統(tǒng)拋出異常的行為一定程度上是等價(jià)的,后續(xù)處理方式也是一樣的,在這里我們使用throw關(guān)鍵字。
throw語(yǔ)句可以多帶帶使用,注意它拋出的是一個(gè)異常實(shí)例。
try{ // do something... throw new Exception("hhh 我是新異常"); // 異常實(shí)例 } catch{ System.out.println("出現(xiàn)異常"); continue; }
當(dāng)我們自行拋出的異常是checked異常的時(shí)候,該throw語(yǔ)句要么是在如上面例子中的try塊中,顯示捕獲,要么是在一個(gè)已經(jīng)用throws聲明會(huì)出現(xiàn)異常的方法中,而如果我們拋出的是runtime異常,那么情況就很簡(jiǎn)單了,它無(wú)需在try塊中,也不需要將對(duì)應(yīng)的方法用throws聲明,如果我們想要處理,就捕獲處理它,不管是在它自身方法體內(nèi),或者是對(duì)應(yīng)方法者,也可以不去理會(huì),當(dāng)然我們自己拋出的異常,通常情況下是要處理的,不然拋出去之后不管最后只能中斷程序運(yùn)行了,只不過(guò)拋出是runtime異常時(shí),在編譯時(shí)沒(méi)有那么嚴(yán)格。
自定義異常類:前面說(shuō)了,系統(tǒng)會(huì)拋出一些普遍意義的異常,那么我們也就沒(méi)必要再自己操心throw了,通常throw的是自定義的異常類。
自定義異常類都應(yīng)該繼承Exception類,或者Exception下的子類如runtime異常
定義異常類的時(shí)候需要提供兩個(gè)構(gòu)造器,一個(gè)無(wú)參構(gòu)造器,一個(gè)帶一個(gè)字符串參數(shù)的構(gòu)造器,這串字符串實(shí)際上是getMessage() 時(shí)返回的異常對(duì)象的詳細(xì)描述信息。
public class myException extends Exception{ public myException(){}; public myException(String msg){ super(msg); } }
catch中throw(拋出)異常:有時(shí)候我們?cè)诒痉椒ㄖ胁蹲搅水惓#覀冎荒芴幚懋惓5囊徊糠郑覀冞€需要?jiǎng)e的方法來(lái)處理或者我們想把產(chǎn)生了異常的這個(gè)信息告訴調(diào)用者,這個(gè)時(shí)候我們通常捕捉了異常后會(huì)在catch塊中拋出我們想拋出的異常
在企業(yè)級(jí)應(yīng)用中,通常對(duì)異常處理分為兩部分:應(yīng)用后臺(tái)打印或者通過(guò)日志記錄異常發(fā)生時(shí)詳細(xì)情況(異常跟蹤棧)和向使用者傳達(dá)某種提示。
java 7 中增強(qiáng)了throw語(yǔ)句:java 7 編譯器執(zhí)行更細(xì)致檢查,檢查拋出的異常的具體類型。
public class Test{ private double initprice = 30.0; // 自定義的異常 // 方法中拋出了異常 所以此處要聲明異常 public void bid(String bidprice) throws AuctionException{ double d =0.0; try{ d = Double.parseDouble(bidprice); } catch(Exception e){ e.printStackTrace(); throw new AuctionException("新異常"); //這里也可以拋出e 但是我們通常會(huì)拋出我們包裝過(guò)后的異常 // 用來(lái)向方法調(diào)用者或者上層提示某種信息 而不會(huì)直接暴露異常的原因 // throw e; // 如果這里throw e 在java7 以前不會(huì)做細(xì)致檢查,throws聲明那里必須聲明為Exception // 但是java7 之后可以聲明為更細(xì)致的異常子類型 } } public static void main(String[] args){ Test test = new Test(); try{ test.bid("df"); } catch(AuctionException ae){ System.err.println(ae.getMessage); } } }
異常鏈:異常轉(zhuǎn)譯與異常鏈
3. 異常處理的一些基本規(guī)則
3.1 異常跟蹤棧
異常是從里向外傳播,可以很方便跟蹤異常的發(fā)生情況,可以用來(lái)調(diào)試程序,但在發(fā)布的程序中,應(yīng)該避免使用,而是將異常進(jìn)行適當(dāng)?shù)奶幚怼?br>3.2 不要過(guò)度使用
異常的運(yùn)行效率會(huì)差一些,因此如果不是那種不可預(yù)期的錯(cuò)誤,應(yīng)該避免使用異常,而是放在正常的業(yè)務(wù)判斷或者處理邏輯中。
3.3 不要使用過(guò)于龐大的try塊
為了更好的判斷發(fā)生的異常的類型,應(yīng)該將大塊try塊分割為多個(gè)可能出現(xiàn)異常的try段落。
3.4 避免catch-all
類似上面的理由,更好的區(qū)分度,更好的異常判斷
3.5 不要忽略異常
避免catch塊為空,或者僅僅打印出異常情況,我們還是要處理異常,比如繞過(guò)異常發(fā)生的地方,或者采用別的替選數(shù)據(jù)或方案,或者提示用戶重新操作,或者重新拋出異常,進(jìn)行異常轉(zhuǎn)譯,重新包裝,交給上層調(diào)用者來(lái)對(duì)該異常進(jìn)行處理。
歡迎加入學(xué)習(xí)交流群569772982,大家一起學(xué)習(xí)交流。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/67755.html
摘要:不受檢查異常為編譯器不要求強(qiáng)制處理的異常,檢查異常則是編譯器要求必須處置的異常。潛在的異常處理器是異常發(fā)生時(shí)依次存留在調(diào)用棧中的方法的集合。當(dāng)運(yùn)行時(shí)系統(tǒng)遍歷調(diào)用棧而未找到合適的異常處理器,則運(yùn)行時(shí)系統(tǒng)終止。異常處理涉及到五個(gè)關(guān)鍵字,分別是。 概念 程序運(yùn)行時(shí),發(fā)生的不被期望的事件,它阻止了程序按照程序員的預(yù)期正常執(zhí)行,這就是異常。 異常是程序中的一些錯(cuò)誤,但并不是所有的錯(cuò)誤都是異常,并...
摘要:異常也就是指程序運(yùn)行時(shí)發(fā)生錯(cuò)誤,而異常處理就是對(duì)這些錯(cuò)誤進(jìn)行處理和控制。有兩個(gè)重要的子類異常和錯(cuò)誤,二者都是異常處理的重要子類,各自都包含大量子類。需要注意的是,一旦某個(gè)捕獲到匹配的異常類型,將進(jìn)入異常處理代碼。 1,異常現(xiàn)象 程序錯(cuò)誤分為三種:1,編譯錯(cuò)誤;2,運(yùn)行時(shí)錯(cuò)誤;3,邏輯錯(cuò)誤。 編譯錯(cuò)誤是因?yàn)槌绦驔](méi)有遵循語(yǔ)法規(guī)則,編譯程序能夠自己發(fā)現(xiàn)并且提示我們錯(cuò)誤的原因和位置,這...
摘要:可以被異常處理機(jī)制使用,是異常處理的核心。非檢測(cè)異常,在編譯時(shí),不會(huì)提示和發(fā)現(xiàn)異常的存在,不強(qiáng)制要求程序員處理這樣的異常。總體來(lái)說(shuō),語(yǔ)言的異常處理流程,從程序中獲取異常信息。處理運(yùn)行時(shí)異常,采用邏輯合理規(guī)避同時(shí)輔助處理。 目錄 什么是Java異常? 當(dāng)一個(gè)Exception在程序中發(fā)生的時(shí)候,JVM是怎么做的呢? 當(dāng)我們編寫(xiě)程序的時(shí)候如何對(duì)待可能出現(xiàn)的異常呢? 正文 1. 什么是J...
摘要:下面是異常處理機(jī)制的語(yǔ)法結(jié)構(gòu)業(yè)務(wù)實(shí)現(xiàn)代碼輸入不合法如果執(zhí)行塊里業(yè)務(wù)邏輯代碼時(shí)出現(xiàn)異常,系統(tǒng)自動(dòng)生成一個(gè)異常對(duì)象,該對(duì)象被提交給運(yùn)行時(shí)環(huán)境,這個(gè)過(guò)程被稱為拋出異常。 Java的異常機(jī)制主要依賴于try、catch、finally、throw和throws五個(gè)關(guān)鍵字, try關(guān)鍵字后緊跟一個(gè)花括號(hào)括起來(lái)的代碼塊(花括號(hào)不可省略),簡(jiǎn)稱try塊,它里面放置可能引發(fā)異常的代碼 catch后對(duì)...
摘要:根據(jù)異常對(duì)象判斷是否存在異常處理。否則,范圍小的異常會(huì)因異常處理完成而無(wú)法處理。異常處理中使用作為異常的統(tǒng)一出口。 參考《第一行代碼java》《java程序設(shè)計(jì)教程》java中程序的錯(cuò)誤有語(yǔ)法錯(cuò)誤、語(yǔ)義錯(cuò)誤。如果是語(yǔ)法性錯(cuò)誤,在編譯時(shí)就可以檢查出來(lái)并解決。語(yǔ)義錯(cuò)誤是在程序運(yùn)行時(shí)出現(xiàn)的,在編譯時(shí)沒(méi)有錯(cuò)誤,但在運(yùn)行時(shí)可能會(huì)出現(xiàn)錯(cuò)誤導(dǎo)致程序退出,這些錯(cuò)誤稱為異常。在沒(méi)有異常處理的情況下,也即...
摘要:為可恢復(fù)的錯(cuò)誤使用檢查型異常,為編程錯(cuò)誤使用非檢查型錯(cuò)誤。檢查型異常保證你對(duì)錯(cuò)誤條件提供異常處理代碼,這是一種從語(yǔ)言到強(qiáng)制你編寫(xiě)健壯的代碼的一種方式,但同時(shí)會(huì)引入大量雜亂的代碼并導(dǎo)致其不可讀。在編程中選擇檢查型異常還是運(yùn)行時(shí)異常。 異常處理是Java 開(kāi)發(fā)中的一個(gè)重要部分。它是關(guān)乎每個(gè)應(yīng)用的一個(gè)非功能性需求,是為了處理任何錯(cuò)誤狀況,比如資源不可訪問(wèn),非法輸入,空輸入等等。Java提供了...
閱讀 3212·2021-11-02 14:44
閱讀 3725·2021-09-02 15:41
閱讀 1661·2019-08-29 16:57
閱讀 1784·2019-08-26 13:38
閱讀 3297·2019-08-23 18:13
閱讀 2104·2019-08-23 15:41
閱讀 1668·2019-08-23 14:24
閱讀 3029·2019-08-23 14:03