简单谈谈java的异常处理(Try Catch Finally)
异常的英文单词是exception,字面翻译就是“意外、例外”的意思,也就是非正常情况。事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误。
一 前言
java异常处理大家都不陌生,总的来说有下面两点:
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(); }; }
本文将在此基础上,更加深入的谈一些细节问题。
二 自定义异常类
java语言为我们提供了很多异常类,但是有时候我们为了写代码的方便还是要自定义的去创造异常类:
class simpleexception extends exception {};
创建好之后我们可以使用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中定义了一个方法a(),让它抛出simpleexception异常,然后我们在main()中调用这个方法,并使用try catch捕获了这个异常:
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
编译执行后的结果,主要看前三行就行了。这里着重说明几点:
1.抛出异常类型的指定:(exception specification)
当我们需要在一个方法中抛出一个异常时,我们使用throw后加某异常类的实例,程序会在此向客户端程序(调用这段代码的程序)抛出对应异常并在此退出(相当于return)。另外需要注意的是,我们必须在定义该方法的时候指明异常类型,比如下面这段代码会抛出simpleexception异常
public void a() throws simpleexception
2.抛出多个异常:
public void a() throws simpleexception,aexception,bexception{ throw new simpleexception(); }
不同的异常类之间用逗号隔开即可,在这种情况下我们不必须throw每个异常类的实例(),但是客户端代码必须要catch到每个异常类:
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异常机制给我们提供的异常信息,而它的载体就是stack trace。
前面的代码中我们直接使用printstacktrace()打印出异常信息,其实我们还可以使用getstacktrace()方法来获取stacktraceelement型的集合,如果你手头有idea的话,你可以先搜索出stacktraceelement类,可以发现它实现了接口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 */
讲的很清楚,这个类的每个实例都是stack trace的一个元素,代表着一个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异常的同时捕获它,然后我们通过getstacktrace()方法得到一个stacktraceelement型的数组,并打印出数组的长度:
7
process finished with exit code 0
我们把代码稍微改一下,不在a中捕获异常了,我们重新定义一个方法b,让它在调用a的同时将异常捕获:
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(); } }
结果如下:
8
process finished with exit code 0
别急,我们再来看点有趣的:
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(); } }
下面是结果:
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()返回一个栈,它包含从调用者(main())到初始抛出异常者(a())的一些基本信息 ,在上面的代码中,我们在c方法中调用a方法时捕获异常并通过throws将其再次抛出(rethrow),调用c方法的方法可以捕获并处理异常,也可以选择继续抛出让更高层次的调用者(靠近栈底)处理。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
先来看一个场景:
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); } }
创建了三个异常类aexception、bexception、cexception,然后在a()中抛出aexception,在b()中捕获aexception并抛出bexception,最后在c()中捕获bexception并抛出cexception,结果打印如下:
cexception: this is c exception at testexception.c(testexception.java:31) at testexception.main(testexception.java:8)
好,我们只看到了cexception的信息,aexception,bexception的异常信息已丢失,这时候异常链的作用就出来了,看代码:
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()方法将异常信息给串联了起来,结果如下:
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
五 后记
其实关于java异常处理还有很多需要探讨的地方,但是由于我经验有限,还不能体会的太深刻,最常用的也就是
try { ... }catch (exception e){ ... }finally { //不管异常会不会被捕捉或者处理都会执行的代码,如关闭io操作 }
但是无论如何我们还是要感谢java给我们提供的异常机制,它好似一个长者,时不时给我们指引道路,也让我们在编码的时候没有那么无聊:)
推荐阅读
-
谈谈Java中try-catch-finally中的return语句
-
day14 异常(try-catch,finally,throws,throw,自定义异常类)-> java基础大神进阶学习总结之19天(知识点全面覆盖,细节)
-
谈谈Java中try-catch-finally中的return语句
-
Java异常处理之try...catch...语句的使用进阶
-
Java中异常处理之try和catch代码块的使用
-
详解Java异常处理中finally子句的运用
-
简单了解Java编程中对异常处理的运用
-
Java异常处理中同时有finally和return语句的执行问题
-
Java异常处理之try...catch...语句的使用进阶
-
详解Java异常处理中finally子句的运用