摘要:一前言异常处理大家都不陌生,总的来说有下面两点抛出异常捕获异常本文将在此基础上,更加深入的谈一些细节问题。
一 前言
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给我们提供的异常机制,它好似一个长者,时不时给我们指引道路,也让我们在编码的时候没有那么无聊:)
我的微信号是aristark,欢迎交流指正!
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/66200.html
摘要:允许存在多个,用于针对不同的异常做不同的处理。表示程序可能需要捕获并且处理的异常。因此,我们应该尽可能的避免通过异常来处理正常的逻辑检查,这样可以确保不会因为发生异常而导致性能问题。异常表中的每一条记录,都代表了一个异常处理器。 showImg(https://segmentfault.com/img/remote/1460000017918154?w=640&h=100); show...
捕获和处理异常 本节描述如何使用三个异常处理程序组件 — try、catch和finally块 — 来编写异常处理程序,然后,解释了Java SE 7中引入的try-with-resources语句,try-with-resources语句特别适用于使用Closeable资源的情况,例如流。 本节的最后一部分将介绍一个示例,并分析各种场景中发生的情况。 以下示例定义并实现名为ListOfNumbe...
摘要:根据异常对象判断是否存在异常处理。否则,范围小的异常会因异常处理完成而无法处理。异常处理中使用作为异常的统一出口。 参考《第一行代码java》《java程序设计教程》java中程序的错误有语法错误、语义错误。如果是语法性错误,在编译时就可以检查出来并解决。语义错误是在程序运行时出现的,在编译时没有错误,但在运行时可能会出现错误导致程序退出,这些错误称为异常。在没有异常处理的情况下,也即...
摘要:例如将型对象转换为类抛出的异常表明向方法传递了一个不合法或不正确的参数。应用程序可以将此类子类化以指示类似的异常。用非法索引访问数组时抛出的异常。 第四阶段 IO 异常处理 没有完美的程序,所以我们需要不断地完善,考虑各种可能性,我们要将除了自己以外的任何用户或者操作者都当成傻子来考虑问题在我们开发过程中 我们运行时常常会遇到 这样java.lang.XxxException的信息,这...
阅读 2660·2021-11-16 11:53
阅读 2726·2021-07-26 23:38
阅读 2062·2019-08-30 15:55
阅读 1736·2019-08-30 13:21
阅读 3610·2019-08-29 17:26
阅读 3286·2019-08-29 13:20
阅读 861·2019-08-29 12:20
阅读 3180·2019-08-26 10:21