转载

java异常处理(Try Catch Finally)

一 前言

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给我们提供的异常机制,它好似一个长者,时不时给我们指引道路,也让我们在编码的时候没有那么无聊:)

我是小火Ari,很幸运你能看到我写的,我的邮箱aristark@126.com,微信:aristark,求联系求勾搭

原文  https://segmentfault.com/a/1190000004681136
正文到此结束
Loading...