转载

Java 解惑系列(四):有意思的 try-catch 顺序问题

问题一:有意思的异常处理

看一下下面的异常处理代码,猜一下会打印什么呢?

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

private static boolean decision() {
    try {
        return true;
    } finally {
        return false;
    }
}

熟悉Java异常处理的你,可能一眼就看出来了,结果是打印 false ,如果你觉得会打印 true 的话,那说明你的基础还是有点小问题的。虽然这里比较简单,但我们还是来简单说明下。

解惑1:

我们既然学过java中的异常处理,那么原因的话也比较简单:

try-finally 块中,无论 try 语句是否是正常结束,是否抛出异常,是否直接返回,最终都会执行 finally 块中的语句;

但有一点需要注意,如果在try语句中执行了 System.exit(0) ,那么便不会走到finally块了,因为该操作会直接停止当前线程。

1. try-finally顺序问题

我们接着来看try-finally的问题,下面来看下try-finally的顺序问题,看下下面的程序会打印什么?

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

private static boolean decision() {
    try {
        System.out.println("---try---");
        return true;
    } finally {
        System.out.println("---finally---");
    }
}

很显然,这里try语句中使用了return语句,而finally中没有return,那么最终打印的结果:

---try---
---finally---
true

和上面相比,也就是说

try会尝试执行return语句,如果finally块中没有return语句,那么try的尝试将会成功;如果finally中有return,那么try的尝试将会被finally替代。

2. try-catch-finally的问题

这次我们加入catch操作来看下(main方法由于都一样,这里就不贴上来了):

private static boolean decision() {
    try {
        System.out.println("---try---");
        int temp = 23 / 0;
        return true;
    } catch (Exception e) {
        System.out.println("---catch---");
        return false;
    } finally {
        System.out.println("---finally---");
    }
}

因为程序有异常,所以一定会走进catch块,那么最终的打印结果想必大家也知道了:

---try---
---catch---
---finally---
false

而如果finally中也有return语句的话呢,会不会也会覆盖掉catch中的return呢?我们简单修改下代码:

private static boolean decision() {
    try {
        System.out.println("---try---");
        int temp = 23 / 0;
        return true;
    } catch (Exception e) {
        System.out.println("---catch---");
        return true;
    } finally {
        System.out.println("---finally---");
        return false;
    }
}

没错,大家的猜想是正确的:

---try---
---catch---
---finally---
false

3. try-catch-finally 语句相加问题

再来看一下最后一个例子。猜测一下下面会打印什么呢?

private static int decision() {
    int temp = 10;
    try {
        System.out.println("--try---");
        return temp += 5;
    } catch (Exception e) {
        System.out.println("---catch---");
    } finally {
        System.out.println("---finally---");
        temp = 25;
    }
    return temp;
}

这里在进行try-catch-finally中的时候虽然有一个运算操作,不过大家应该一眼就看出来了:

--try---
---finally---
15

这个和上面的try-catch流程类似,因为try语句中有return语句,所以永远不会走到最下面的return语句,并且因为finally里没有return,所以无论我们在finally里怎么修改temp的值,最终返回的仍然是try语句中的temp的值。

这里可能还需要注意的就是 return temp += 5 ;如果finally中也有return的话,那么最终将执行finally里的return:

private static int decision() {
    int temp = 10;
    try {
        System.out.println("--try---");
        return temp += 5;
    } catch (Exception e) {
        System.out.println("---catch---");
    } finally {
        System.out.println("---finally---");
        return temp;
    }
}

很显然,这里会执行finally块中的return,但最终的temp的值是由 return temp += 5 这里计算出来的,这里会分成两步计算: temp + 5;return temp; ,所以最终返回的temp仍然是15。

问题二:checked与unchecked异常

看下下面的代码会打印什么呢?

public static void main(String[] args) {
    try {
        System.out.println("Hello World");    
    } catch (IOException e) {
        System.out.println("exception");
    }
}

猜测一下这个程序会打印什么呢,会打印 Hello World 么?很遗憾,这个程序编译不通过,更谈不上打印些什么了。

解惑2:

这个问题原因也是很简单,因为:

  • Java语言规范将继承自Error类或者RuntimeException类的所有异常称为 未检查异常(unchecked) ,而所有其他的异常则称为 已检查异常(checked)

  • 针对checked异常也就是说,一个方法不仅需要告诉编译器将要返回什么值,还要告诉编译器有可能发生什么错误 ;所以Java规范要求,如果cache语句要捕获一个checked异常,那么相应的try子句便要抛出对应checked异常或子类型的异常;

  • 因为 IOException 属于checked异常,所以这里编译不通过;

那么来看一下下面的程序呢,会打印什么呢?

public static void main(String[] args) {
    try {
        System.out.println("Hello World");
    } catch (Exception e) {
        System.out.println("exception");
    }
}

很显然,这个程序肯定是可以编译通过的,并且会打印对应的 Hello world ,因为:

在Java中,捕获 Exception 或者 Throwable 的catch语句是合法的;因为Exception本身是个基础类,不属于unchecked异常,也不属于checked异常,在Java中直接捕获 Exception 或者 Throwable 的都是没问题的;

那我们再来看一下面的程序,最终会通过编译么?

public class Main implements Type3{
    public static void main(String[] args) {
        Type3 main = new Main();
        main.f();
    }

    @Override
    public void f() {
        System.out.println("hello world");
    }
}

interface Type1 {
    void f() throws IOException;
}
interface Type2 {
    void f() throws InterruptedException;
}
interface Type3 extends Type1, Type2 {}

首先, IOExceptionInterruptedException 都是checked异常,Type3继承自上面这两个接口,看上去应该编译不通过。但很遗憾,这个程序不但可以编译通过,而且运行没有问题。虽然我们在学习异常的时候,了解到:

一个方法要么捕获其方法体可以抛出的所有受检查异常,要么声明它将抛出这些异常;

就本例而言, Main 调用了 f() 方法,但却没有处理这两个受检查异常,但是:

一个方法可以抛出的受检查异常集合,是它所适用的所有类型声明要抛出的受检查异常集合的交集,而不是合集;

简单来说,这里如果会抛出的话,只能抛出这两个异常的交集,但因为没有交集,所以无需抛出,自然也能编译通过,成功运行。

原文  https://mp.weixin.qq.com/s/bTHMNqpO2PobuMosCF9NSA
正文到此结束
Loading...