天天看點

try-catch-finally-return執行順序

作者:liangcao

通過本文可以收獲:

1. try-catch-finally 執行順序

2. 如何影響傳回值

着急的朋友可以直接到底部檢視結論,不過建議一步一步跟着我走一遍

Talk is cheap,show you my code
  • 情形一
private static void tcfOrder() {
        try {
            System.out.println("try...");
        } catch (Exception e) {
            System.out.println("catch...");
        } finally {
            System.out.println("finally...");
        }
        System.out.println("method end...");
    }           

輸出:

try...
finally...
method end...           

?為什麼沒有“catch...",因為沒有異常發生,是以 catch 代碼塊沒有執行

  • 情形二
private static void tcfOrderWithException() {
        try {
            System.out.println("try...");
            int i = 1 / 0;
        } catch (Exception e) {
            System.out.println("catch...");
        } finally {
            System.out.println("finally...");
        }
        System.out.println("method end...");
    }           

輸出:

try...
catch...
finally...
method end...           

?try-catch-finally 必須同時出現嗎

來個 try

try-catch-finally-return執行順序

代碼看不到效果

可以看到,IDE(Integrated Development Environment)已經給提示了 try 是不能單獨使用的,得有 catch 或 finally

private static void tcf() {
        try {
            System.out.println("try...");
            int i = 1 / 0;
        } catch (Exception e) {
            e.printStackTrace(); // 與上面不同的是加入了異常時的堆棧列印
            System.out.println("catch...");
        }
        System.out.println("method end...");
    }
           

輸出:

try...
catch...
method end...
// 同時多了下面的輸出,可以看到具體的異常資訊
java.lang.ArithmeticException: / by zero           

?ArithmeticException 又是什麼

try-catch-finally-return執行順序
try-catch-finally-return執行順序

可以看到是 Exception 的一個子類,而且 Exception 的子類還有很多。那我可以 catch ArithmeticException 嗎?

private static void tcfWithArithmeticException() {
        try {
            System.out.println("try...");
            int i = 1 / 0;
        } catch (ArithmeticException e) {
            e.printStackTrace();
            System.out.println("catch...");
        }
        System.out.println("method end...");
    }           

輸出:

try...
catch...
method end...

java.lang.ArithmeticException: / by zero           

?這麼看來和 catch Exception 是沒有差別的呀,那它存在的價值是什麼

private static void tcfWithException() {
        try {
            System.out.println("try...");
            int i = 1 / 0;
        } catch (ArithmeticException e) {
            System.out.println("catch ArithmeticException...");
        } catch (Exception ee) 
            System.out.println("catch exception...");
        }
        System.out.println("method end...");
    }

輸出:
try...
catch ArithmeticException...
method end...
--------------
private static void tcfWithException() {
        try {
            System.out.println("try...");
            int i = 1 / 0;
        }  catch (Exception ee) {
            System.out.println("catch exception...");
        } catch (ArithmeticException e) {
            System.out.println("catch ArithmeticException...");
        }
        System.out.println("method end...");
    }

輸出:
try...
catch exception...
method end...           

可以看到,一個 try 可以配多個 catch

? 多個 catch 順序有關嗎

private static void tcfWithException() {
        try {
            System.out.println("try...");
            int i = 1 / 0;
        } catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) {
          // 這裡将上面的   ArithmeticException 換成 Exception 的另外的一個子類
          System.out.println("another exception");
        }
        catch (Exception ee) {
            System.out.println("catch exception...");
        }
        System.out.println("method end...");
    }
-------
  private static void tcfWithException() {
        try {
            System.out.println("try...");
            int i = 1 / 0;
        }  catch (Exception ee) {
            System.out.println("catch exception...");
        } catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) {
          // 這裡将上面的   ArithmeticException 換成 Exception 的另外的一個子類
          System.out.println("another exception");
        }
        System.out.println("method end...");
    }           

均輸出:

try...
catch exception...
method end...           

并沒有輸出"another exception"

原來我們的 catch 是按照順序比對的,而且是”懶惰“模式比對,比對到一個滿足的之後,其後所有 catch 均被跳過,是以我們一定按照可預知異常,到不可知異常順序編寫(從小到大 catch) 這裡就像是用魚網捕魚,不能上來就用最密集的網打撈

可以看到

catch exception_1 一定會同時捕獲到 exception_2 和 exception_3

catch exception_2 一定會同時捕獲到 exception_3 但是會放過 exception_1

catch exception_3 一定會同時捕獲到 exception_3 但是會放過 exception_1 和 exception_2

try-catch-finally-return執行順序

再回到 try-catch-finally 必須同時出現的問題上,目前我們已經知道,可以沒有 finally 了,而且一個 try 可以配多個 catch

private static void tcf() {
        try {
            System.out.println("try...");
//            int i = 1 / 0;
        } finally {
            System.out.println("finally...");
        }
        System.out.println("method end...");
    }           

輸出:

try...
finally...
method end...           

可以多個 finally 嗎

try-catch-finally-return執行順序

答案是不可以的

如果 try-finally 有異常呢

private static void tcf() {
        try {
            System.out.println("try...");
            int i = 1 / 0;
        } finally {
            System.out.println("finally...");
        }
        System.out.println("method end...");
    }           

輸出:

try...
finally...

同時
Exception in thread "main" java.lang.ArithmeticException: / by zero           

可以看到 finally 代碼塊被執行了,但是 "method end..." 沒有出現,這是因為,目前方法出現異常,如果目前塊沒有處理,就會像上層繼續抛,直到有代碼處理,如果一直沒有,就會到 jvm 層,程式終止執行,列印異常

好了,到目前為止我們的結論是:

  1. 一個 try 必須有至少一個 catch 或 finally
  2. 一個 try 可以比對至少一個 catch
  3. 一個 try 可以比對最多一個 finally
  4. 如果有 finally,無論是否有異常,都會執行 finally 代碼塊

如果此時,在上面的例子中加入 return 會出現什麼呢?

private static int tcfOrderWithReturn() {
        int result = 1;
        try {
            result = 2;
            System.out.println("try value: " + result);
            return result;
        } catch (Exception e) {
            result = 3;
            System.out.println("catch value: " + result);
        } finally {
            result = 4;
            System.out.println("finally value: " + result);
        }
        System.out.println("method end value: " + result);
        return result;
    }           

輸出:

try value: 2
finally value: 4

method return: 2 // main 函數輸出           

可以看到,雖然運作了 finally 代碼塊,但是傳回值并沒有受到影響

如果發生了異常呢

private static int tcfOrderWithReturn() {
        int result = 1;
        try {
            result = 2;
            System.out.println("try value: " + result);
            int i = 1 / 0;
            return result;
        } catch (Exception e) {
            result = 3;
            System.out.println("catch value: " + result);
        } finally {
            result = 4;
            System.out.println("finally value: " + result);
        }
        System.out.println("method end value: " + result);
        return result;
    }           

輸出:

try value: 2
catch value: 3
finally value: 4
method end value: 4
method return: 4           

傳回值是最後的 return(16行) ,并不是 7行的 return

如果 catch 中 return 呢

try-catch-finally-return執行順序

就會提前終止函數的執行,是以後面代碼塊是沒有機會執行的

private static int tcfOrderWithReturn() {
        int result = 1;
        try {
            result = 2;
            System.out.println("try value: " + result);
            int i = 1 / 0;
            return result;
        } catch (Exception e) {
            result = 3;
            System.out.println("catch value: " + result);
            return result;
        } finally {
            result = 4;
            System.out.println("finally value: " + result);
        }
    }           

輸出:

try value: 2
catch value: 3
finally value: 4
method return: 3           

可以看到隻要有了 return ,後面再改變值是無效的,這裡是基本變量,如果換成對象(引用變量)呢?

private static List tcfOrderWithReturn() {
        List result = new ArrayList();
        try {
            result.add(2);
            System.out.println("try value: " + result);
            int i = 1 / 0;
            return result;
        } catch (Exception e) {
            result.add(3);
            System.out.println("catch value: " + result);
            return result;
        } finally {
            result.add(4);
            System.out.println("finally value: " + result);
        }
    }           

輸出:

try value: [2]
catch value: [2, 3]
finally value: [2, 3, 4]
method return: [2, 3, 4]           

可以看到雖然在 catch 中 return 了,但是 finally 中的修改生效了

同理,如果 finally 中 return 了,會覆寫 try-catch 中的 return, 但是一般是不建議這麼做的,finally 是為了讓我們釋放資源,處理後續工作的。

好了,到目前為止我們的結論是:

  1. 一個 try 必須有至少一個 catch 或 finally
  2. 一個 try 可以比對至少一個 catch
  3. 一個 try 可以比對最多一個 finally
  4. 如果有 finally,無論是否有異常,都會執行 finally 代碼塊
  5. try 中 return 基本變量類型,後續再 catch 或 finally 中更改傳回值是無效的
  6. try 中 return 引用類型變量,後續再 catch 或 finally 中更改傳回值是有效的

執行順序:

try-catch-finally,縮寫 T-C-F (記不住?”它<Ta>吃<Chi>飯<Fan>“)

如果有 return 呢:

try(return)-catch(return)-finally(return),縮寫 Tr-Cr-Fr, 還是”它吃飯“,後面優先級越來越高

PS:

?finally 中的代碼塊真的會永遠執行嗎

如果是正常情況下,是永遠會執行的。

不正常情況有:

  1. try 或 catch 中執行了死循環
  2. try 或 catch 中執行了 System.exit(0)
  3. jvm 或 機器崩潰斷電了
  4. ...

注意:

  1. 不建議在 catch-finally 中操作傳回值
  2. 有 try 必有 catch,不要吞噬異常
  3. catch 中列印 error 級别日志,列印堆棧資訊
  4. 如果不清楚異常範圍”try 大“沒有關系,首先保證程式健壯性(挖坑,如果對于try-catch對性能影響感興趣多的話,我補充一個性能影響篇)