Java中的异常体系

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/xufei_0320/article/details/84992968

吹逼两小时,代码五分钟。平常写代码中异常无处不在,早就练就了我们try{...} actch(){...}finally{...}一把梭的深厚功力。毕竟它看起来真的很简单,我们用着也蛮顺心,但是你真的了解它吗,真的不会出错吗?

Java异常体系

下面是Java中的异常继承图:

Throwable是整个异常体系中的顶级父类,它拥有两个子类,分别是ErrorException

Error是由机器底层抛出的错误,我们无法处理的,比如OutOfMemoryError,当遇到这类错误时,JVM会直接终止进程,应用终止。此类异常我们不要去捕获,因为捕获了也处理不了。

Exception是程序可以处理的错误,主要分为运行时异常,和非运行时异常。或者叫做受检异常和非受检异常。

运行时异常都是RuntimeException及其子类,比如,NullPointExceptionArrayIndexOutOfBoundsException等等,这类的异常属于非受检异常(UnChecked),我们可以对其捕获处理,也可以不处理,而我们一般也不会做处理的,因为这类错误通常是我们逻辑错误所导致的,我们应该尽量避免此类Bug

而非运行时异常,比如IOExceptionEOFException等等所有非RuntimeException及其子类,都是非运行时异常,也就是受检异常(Checked),当方法抛出了这类异常,则调用者必须在调用该方法时对其进行处理,否则将无法编译。

异常处理

那么我们该怎么去处理异常呢,这时候自然而然会想到try-catch-finally,我们来下面的代码:

public static void main(String[] args) {
        System.out.println(test1());
    }

    public static boolean test1() {
        boolean flag = true;
        try {
            flag = test2();
        } catch (Exception e) {
            System.out.println("[test1] catch exception result=" + flag);
            flag = false;
            throw e;
        } finally {
            System.out.println("[test1] finally result=" + flag);
            return flag;
        }
    }

    public static boolean test2() throws Exception {
        boolean flag = true;
        try {
            // 分别调用test3()和test4()
            flag = test4();
            if (!flag) {
                return false;
            }
            System.out.println("[test2] result=" + flag);
            return flag;
        } catch (Exception e) {
            System.out.println("[test2] catch exception result=" + flag);
            flag = false;
            throw e;
        } finally {
            System.out.println("[test2] finally result=" + flag);
            return flag;
        }
    }

    public static boolean test3() throws Exception {
        boolean flag = true;
        try {
            System.out.println("[test3] result=" + flag);
            return true;
        } catch (Exception e) {
            System.out.println("[test3] catch exception result=" + flag);
            flag = false;
            throw e;
        } finally {
            System.out.println("[test3] finally result=" + flag);
            return flag;
        }
    }

    public static boolean test4() throws Exception {
        boolean flag = true;
        try {
            int a = 2 / 0;
            System.out.println("[test4] result=" + flag);
            return true;
        } catch (Exception e) {
            System.out.println("[test4] catch exception result=" + flag);
            flag = false;
            throw e;
        } finally {
            System.out.println("[test4] finally result=" + flag);
            return flag;
        }
    }

当其中test2()方法分别调用test3()teset4(),打印顺序会是怎么样的呢,不如先思考一下。

调用test3()时,打印结果:

[test3] result=true
[test3] finally result=true
[test2] result=true
[test2] finally result=true
[test1] finally result=true
true

调用test4()时,打印结果:

[test4] catch exception result=true
[test4] finally result=false
[test2] finally result=false
[test1] finally result=false
false

不知道这段代码的实际执行结果和你所想的有没有出入,众所周知的是,当try{···}块中的代码执行未发生异常,贼执行finally{...}块中的代码,如果执行出现异常,则会先执行catch(){...}中的代码,再执行finally{...}中的代码。但是当这些代码块中出现returnthrow则会发生变化。

当try{···}块或者catch(){…}块中有return、throw时,在return或者throw执行前回优先执行finally{…}代码块中的内容

特别提醒,finally中禁止使用return,这里使用,只是为了演示用。

所以当finally中有throw或者return时,它会覆盖try块中和catch块中的throw以及return,从而出现异常屏蔽现象,比如下面这段代码:

public static void main(String[] args) {
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            throw new RuntimeException("catch");
        } finally {
            throw new RuntimeException("finally");
        }
    }

它将打印出:

Exception in thread "main" java.lang.RuntimeException: finally
	at top.felixu.exception.ExceptionDemo1.main(ExceptionDemo1.java:14)

catch块中的异常就被覆盖了,这种现象还是有可能出现在try-catch-finally捕获处理流相关操作中,在finally中手动关闭流出现异常时,但是在JDK7版本以后,try-with-resource可以帮我们自动关闭流了。在JDK7中,所有的IO类都实现了AutoCloseable接口,并且需要实现其中的close()函数,资源释放过程需要在该函数中完成。那么,编译器在编译时,会自动添加finally代码块,并将close()函数中的资源释放代码加入finally代码块中,从而提高代码可读性。这里就不具体介绍了。

异常处理约定

  1. 对于非受检异常,我们不要去捕获处理,而是通过测试和review代码来规避此类问题。
  2. 异常不要用来做流程控制,条件控制,因为异常处理的效率比分支处理要低。
  3. 对大块代码的try-catch是不负责的行为,我们要区分稳定代码已经不稳定代码。
  4. 捕获异常是为了处理异常,如果捕获了什么都不处理,不如不捕获,将其抛给其上层调用者。而最上层的调用者必须处理,防止用户看到无法理解的异常信息。
  5. try块中有事物代码,则catch到异常要手动回滚,或者抛出异常,让AOP框架来回滚,否则会吞掉异常,导致异常却未回滚。
  6. 不能在finally中使用return
  7. 捕获异常必须与所抛出的异常匹配,或者所捕获的异常是抛出异常的父类。

猜你喜欢

转载自blog.csdn.net/xufei_0320/article/details/84992968
今日推荐