java异常处理(Try Catch Finally)

一 前言

java异常处理你们都不陌生,总的来讲有下面两点: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();
    };
}

本文将在此基础上,更加深刻的谈一些细节问题。app

二 自定义异常类

java语言为咱们提供了不少异常类,可是有时候咱们为了写代码的方便仍是要自定义的去创造异常类:this

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异常code

public void a() throws SimpleException

2.抛出多个异常:接口

public void a() throws SimpleException,AException,BException{
        throw new SimpleException();
        
    }

不一样的异常类之间用逗号隔开便可,在这种状况下咱们没必要须throw每一个异常类的实例(),可是客户端代码必需要catch到每一个异常类:ci

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,欢迎交流指正!

相关文章
相关标签/搜索