簡單談?wù)刯ava的異常處理(Try Catch Finally)
異常的英文單詞是exception,字面翻譯就是“意外、例外”的意思,也就是非正常情況。事實(shí)上,異常本質(zhì)上是程序上的錯(cuò)誤,包括程序邏輯錯(cuò)誤和系統(tǒng)錯(cuò)誤。
一 前言
java異常處理大家都不陌生,總的來說有下面兩點(diǎn):
1.拋出異常:throw exception
class SimpleException{ public void a() throws Exception{ throw new Exception(); }; }
2.捕獲異常:
public class MyException { public static void main(String[] args){ MyException e = new MyException(); SimpleException se = new SimpleException(); try { se.a(); } catch (Exception e1) { e1.printStackTrace(); } } } class SimpleException{ public void a() throws Exception{ throw new Exception(); }; }
本文將在此基礎(chǔ)上,更加深入的談一些細(xì)節(jié)問題。
二 自定義異常類
java語言為我們提供了很多異常類,但是有時(shí)候我們?yōu)榱藢懘a的方便還是要自定義的去創(chuàng)造異常類:
class SimpleException extends Exception {};
創(chuàng)建好之后我們可以使用try catch捕獲它:
public class MyException { public static void main(String[] args){ MyException e = new MyException(); try { e.a(); } catch (SimpleException e1) { e1.printStackTrace(); } } public void a() throws SimpleException{ throw new SimpleException(); } } class SimpleException extends Exception {};
我們在MyException中定義了一個(gè)方法a(),讓它拋出SimpleException異常,然后我們在main()中調(diào)用這個(gè)方法,并使用try catch捕獲了這個(gè)異常:
SimpleException at MyException.a(MyException.java:15) at MyException.main(MyException.java:8) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) Process finished with exit code 0
編譯執(zhí)行后的結(jié)果,主要看前三行就行了。這里著重說明幾點(diǎn):
1.拋出異常類型的指定:(exception specification)
當(dāng)我們需要在一個(gè)方法中拋出一個(gè)異常時(shí),我們使用throw后加某異常類的實(shí)例,程序會在此向客戶端程序(調(diào)用這段代碼的程序)拋出對應(yīng)異常并在此退出(相當(dāng)于return)。另外需要注意的是,我們必須在定義該方法的時(shí)候指明異常類型,比如下面這段代碼會拋出SimpleException異常
public void a() throws SimpleException
2.拋出多個(gè)異常:
public void a() throws SimpleException,AException,BException{ throw new SimpleException(); }
不同的異常類之間用逗號隔開即可,在這種情況下我們不必須throw每個(gè)異常類的實(shí)例(),但是客戶端代碼必須要catch到每個(gè)異常類:
public class MyException { public static void main(String[] args){ MyException e = new MyException(); try { e.a(); } catch (SimpleException e1) { e1.printStackTrace(); } catch (BException e1) { e1.printStackTrace(); } catch (AException e1) { e1.printStackTrace(); } } public void a() throws SimpleException,AException,BException{ throw new SimpleException(); } } class SimpleException extends Exception {}; class AException extends Exception{} class BException extends Exception{}
三 stack trace
無論是拋出異常,或者是捕獲處理異常,我們的目的是為了寫出更健壯的程序,這很大程度上依賴于java異常機(jī)制給我們提供的異常信息,而它的載體就是stack trace。
前面的代碼中我們直接使用printStackTrace()打印出異常信息,其實(shí)我們還可以使用getStackTrace()方法來獲取StackTraceElement型的集合,如果你手頭有IDEA的話,你可以先搜索出StackTraceElement類,可以發(fā)現(xiàn)它實(shí)現(xiàn)了接口Serializable ,再看看它的類描述:
/** * An element in a stack trace, as returned by {@link * Throwable#getStackTrace()}. Each element represents a single stack frame. * All stack frames except for the one at the top of the stack represent * a method invocation. The frame at the top of the stack represents the * execution point at which the stack trace was generated. Typically, * this is the point at which the throwable corresponding to the stack trace * was created. * * @since 1.4 * @author Josh Bloch */
講的很清楚,這個(gè)類的每個(gè)實(shí)例都是stack trace的一個(gè)元素,代表著一個(gè)stack frame,stack trace是由getStackTrace()方法返回的。后邊的我試著翻譯了幾遍,都覺得不好,還是直接上代碼才能說清楚:
public class MyException { public static void main(String[] args){ MyException e = new MyException(); e.a(); public void a(){ try { throw new Exception(); } catch (Exception e) { StackTraceElement[] ste = e.getStackTrace(); System.out.println(ste.length); } } }
我們定義了方法a,讓它拋出Exception異常的同時(shí)捕獲它,然后我們通過getStackTrace()方法得到一個(gè)StackTraceElement型的數(shù)組,并打印出數(shù)組的長度:
7
Process finished with exit code 0
我們把代碼稍微改一下,不在a中捕獲異常了,我們重新定義一個(gè)方法b,讓它在調(diào)用a的同時(shí)將異常捕獲:
public class MyException { public static void main(String[] args){ MyException e = new MyException(); e.b(); } public void b(){ try { a(); } catch (Exception e) { StackTraceElement[] ste = e.getStackTrace(); System.out.println(ste.length); } } public void a() throws Exception{ throw new Exception(); } }
結(jié)果如下:
8
Process finished with exit code 0
別急,我們再來看點(diǎn)有趣的:
public class MyException { public static void main(String[] args){ MyException exception = new MyException(); try { exception.c(); } catch (Exception e) { StackTraceElement[] ste = e.getStackTrace(); System.out.println(ste.length); System.out.println("---------------------------------------------------------------"); for (StackTraceElement s : e.getStackTrace()){ System.out.println(s.getClassName()+":method "+s.getMethodName()+" at line"+s.getLineNumber()); } System.out.println("---------------------------------------------------------------"); } } public void c() throws Exception{ try { a(); }catch (Exception e){ throw e; } } public void a() throws Exception{ throw new Exception(); } }
下面是結(jié)果:
8 --------------------------------------------------------------- MyException:method a at line43 MyException:method c at line39 MyException:method main at line9 sun.reflect.NativeMethodAccessorImpl:method invoke0 at line-2 sun.reflect.NativeMethodAccessorImpl:method invoke at line57 sun.reflect.DelegatingMethodAccessorImpl:method invoke at line43 java.lang.reflect.Method:method invoke at line606 com.intellij.rt.execution.application.AppMain:method main at line144 --------------------------------------------------------------- Process finished with exit code 0
也就是說,getStackTrace()返回一個(gè)棧,它包含從調(diào)用者(main())到初始拋出異常者(a())的一些基本信息 ,在上面的代碼中,我們在c方法中調(diào)用a方法時(shí)捕獲異常并通過throws將其再次拋出(rethrow),調(diào)用c方法的方法可以捕獲并處理異常,也可以選擇繼續(xù)拋出讓更高層次的調(diào)用者(靠近棧底)處理。rethrow雖然很方便,但存在著一些問題,我們看下面這段代碼:
public class MyException { public static void main(String[] args){ MyException exception = new MyException(); try { exception.c(); } catch (Exception e) { e.printStackTrace(System.out); } } public void c() throws Exception{ try { a(); }catch (Exception e){ throw e; } } public void a() throws Exception{ throw new Exception("Exception from a()"); } } java.lang.Exception: Exception from a() at MyException.a(MyException.java:40) at MyException.c(MyException.java:30) at MyException.main(MyException.java:21)
我們在c中重新拋出e,在main中使用 e.printStackTrace()打印出來,可以看到打印出來stack trace還是屬于a的,如果我們想把stack trace變成c的可以這么寫:
public class MyException { public static void main(String[] args){ MyException exception = new MyException(); try { exception.c(); } catch (Exception e) { e.printStackTrace(System.out); } } public void c() throws Exception{ try { a(); }catch (Exception e){ // throw e; throw (Exception)e.fillInStackTrace(); } } public void a() throws Exception{ throw new Exception("Exception from a()"); } } java.lang.Exception: Exception from a() at MyException.c(MyException.java:22) at MyException.main(MyException.java:10)
四 異常鏈 Exception chaining
先來看一個(gè)場景:
public class TestException { public static void main(String[] args){ TestException testException = new TestException(); try { testException.c(); } catch (CException e) { e.printStackTrace(); } } public void a() throws AException{ AException aException = new AException("this is a exception"); throw aException; } public void b() throws BException{ try { a(); } catch (AException e) { throw new BException("this is b exception"); } } public void c() throws CException{ try { b(); } catch (BException e) { throw new CException("this is c exception"); } } } class AException extends Exception{ public AException(String msg){ super(msg); } } class BException extends Exception{ public BException(String msg){ super(msg); } } class CException extends Exception{ public CException(String msg){ super(msg); } }
創(chuàng)建了三個(gè)異常類AException、BException、CException,然后在a()中拋出AException,在b()中捕獲AException并拋出BException,最后在c()中捕獲BException并拋出CException,結(jié)果打印如下:
CException: this is c exception at TestException.c(TestException.java:31) at TestException.main(TestException.java:8)
好,我們只看到了CException的信息,AException,BException的異常信息已丟失,這時(shí)候異常鏈的作用就出來了,看代碼:
public class TestException { public static void main(String[] args){ TestException testException = new TestException(); try { testException.c(); } catch (CException e) { e.printStackTrace(); } } public void a() throws AException{ AException aException = new AException("this is a exception"); throw aException; } public void b() throws BException{ try { a(); } catch (AException e) { // throw new BException("this is b exception"); BException bException = new BException("this is b exception"); bException.initCause(e); throw bException; } } public void c() throws CException{ try { b(); } catch (BException e) { // throw new CException("this is c exception"); CException cException = new CException("this is c exception"); cException.initCause(e); throw cException; } } } class AException extends Exception{ public AException(String msg){ super(msg); } } class BException extends Exception{ public BException(String msg){ super(msg); } } class CException extends Exception{ public CException(String msg){ super(msg); } }
我們用initCause()方法將異常信息給串聯(lián)了起來,結(jié)果如下:
CException: this is c exception at TestException.c(TestException.java:35) at TestException.main(TestException.java:8) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) Caused by: BException: this is b exception at TestException.b(TestException.java:24) at TestException.c(TestException.java:32) ... 6 more Caused by: AException: this is a exception at TestException.a(TestException.java:15) at TestException.b(TestException.java:21) ... 7 more Process finished with exit code 0
五 后記
其實(shí)關(guān)于java異常處理還有很多需要探討的地方,但是由于我經(jīng)驗(yàn)有限,還不能體會的太深刻,最常用的也就是
try { ... }catch (Exception e){ ... }finally { //不管異常會不會被捕捉或者處理都會執(zhí)行的代碼,如關(guān)閉IO操作 }
但是無論如何我們還是要感謝java給我們提供的異常機(jī)制,它好似一個(gè)長者,時(shí)不時(shí)給我們指引道路,也讓我們在編碼的時(shí)候沒有那么無聊:)
相關(guān)文章
Java并發(fā)編程之性能、擴(kuò)展性和響應(yīng)
這篇文章主要介紹了Java并發(fā)編程之性能、擴(kuò)展性和響應(yīng),重點(diǎn)在于多線程應(yīng)用程序的性能問題,給性能和擴(kuò)展性下一個(gè)定義,然后再仔細(xì)學(xué)習(xí)一下Amdahl法則,感興趣的小伙伴們可以參考一下2016-02-02解決Mybatis-Plus操作分頁后數(shù)據(jù)失效問題
這篇文章主要介紹了解決Mybatis-Plus操作分頁后數(shù)據(jù)失效問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11JavaWeb中請求轉(zhuǎn)發(fā)和請求重定向的區(qū)別以及使用
今天帶大家學(xué)習(xí)JavaWeb的相關(guān)知識,文章圍繞著JavaWeb中請求轉(zhuǎn)發(fā)和請求重定向的區(qū)別以及使用展開,文中有非常詳細(xì)的介紹,需要的朋友可以參考下2021-06-06SpringBoot多數(shù)據(jù)庫連接(mysql+oracle)的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot多數(shù)據(jù)庫連接(mysql+oracle)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03Mybatis傳單個(gè)參數(shù)和<if>標(biāo)簽同時(shí)使用的問題及解決方法
這篇文章主要介紹了Mybatis傳單個(gè)參數(shù)和<if>標(biāo)簽同時(shí)使用的問題及解決方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-05-05Spring?框架的?MethodInterceptor?簡介及示例代碼
MethodInterceptor接口定義了一個(gè)方法Object?intercept(Object?obj,?Method?method,?Object[]?args,?MethodProxy?proxy)?,該方法在代理對象的方法被調(diào)用時(shí)被觸發(fā),這篇文章主要介紹了Spring?框架的?MethodInterceptor?簡介及示例代碼,需要的朋友可以參考下2023-09-09Java 1,2,3,4能組成多少個(gè)互不相同且無重復(fù)數(shù)字的實(shí)現(xiàn)代碼
這篇文章主要介紹了Java 1,2,3,4能組成多少個(gè)互不相同且無重復(fù)數(shù)字的實(shí)現(xiàn)代碼,需要的朋友可以參考下2017-02-02java模擬TCP通信實(shí)現(xiàn)客戶端上傳文件到服務(wù)器端
這篇文章主要為大家詳細(xì)介紹了java模擬TCP通信實(shí)現(xiàn)客戶端上傳文件到服務(wù)器端,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10