try catch finally 的底層原理

來源:https://blog.csdn.net/lwd512768098/article/details/114728720

昨晚參加一個面試,被問到 try catch finally 相關的知識,雖然之前瞭解過其中奇怪的用法,特別有 return 的情況,但是由於時間久遠,完全忘了,導致這個問題回答不是很好,只是知道 finally 無論是否發生異常都會執行的(但是忘了含有 return 的情況是怎麼處理的了),finally 常常用來關閉一些資源,像文件,連接等。

finally 終究執行

我們先來驗證一下 finally 終究執行

    public static int test() { 
        int i = 1;
        try { 
            i++;
        } finally { 
            System.out.println("finally yeah!");
        }
        return i;
    }

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

輸出爲

    public static int test() { 
        int i = 1;
        try { 
            i++;
            throw new Exception();
        } catch (Exception e) { 
            System.out.println("Exception yeah!");
        } finally { 
            System.out.println("finally yeah!");
        }
        return i;
    }

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

從上面兩個例子中我們可以看到,無論是正確運行 try 塊還是在 try 塊發生了異常,finally 塊都是被執行的

帶有 return 的情況

finally 不帶 return 的情況

對於 return 情況的話,只要記住一句話就行,如果 finally 裏面有 return, 那麼就會覆蓋 try 塊或者 catch 裏面的 return 內容,否則的話,在執行 finally 的內容之前 (try 塊或者 catch 裏面有 return 語句),會計算好 try 或者 catch 裏面的 return 表達式的值,然後保存到另一個局部變量,當執行 fianlly 的時候,會重新加載這個局部變量作爲返回值,因此在 finally 的操作不會影響返回值,下面我們字節碼來看看內部的工作原理。

    public static int test() { 
        int i = 1;
        try { 
            i++;
            return i;
        } finally { 
            i++;
            System.out.println("finally yeah!");
        }
    }

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

第一個問題:在 try 塊中含有 return 語句,你覺得 finally 語句還有執行麼?
第二個問題:最終結果是多少?

從上面可以看到,儘管 try 塊裏面有 return, 但是 finally 塊仍然執行了,你肯定會好奇,那爲啥返回值是 2 呢,finally 裏面不是還進行了 i++ 麼。

簡單分析一下,從結果看,finally 仍然執行,說明 try 的 return 語句是還沒執行的,下面我們看看字節碼。

上面圖片已經標註很清楚了,如果還不明白的話,可以先去看一下這幾個字節碼命令,還是挺簡單的,注意的是,JVM 操作都是基於操作數棧的。

總結一下,總體的意思就是,當執行完 try 塊的時候,會計算 return 表達式的值,然後把這個返回值存在另一個臨時變量裏面,最後返回的時候會重新讀取存放的變量,因此在 finally 後面無論如何修改,都不會影響返回值(除非 finally 裏面含有 return, 這個我們在下面討論)

finally 帶有 return

上面的情況是 try 或者 catch 裏面含有 return,finally 沒有 return, 那麼 fianlly 裏面如何修改返回值是不會影響最後的返回值。下面討論 finally 裏面含有 return 的情況。

    public static int test() { 
        int i = 1;
        try { 
            i++;
            return i;
        } finally { 
            i++;
            System.out.println("finally yeah!");
            return i;
        }
    }

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

相信各位能夠正常猜出結果了

沒錯,返回了 3,說明 finally 對 i 的修過是其效果的,我們從字節碼看看原因:

從上圖可以看到,在返回之前,加載的是 0 槽位的變量,這時 0 槽位的變量的值是 3,1 槽位的變量是 2,所以返回的 3。

由上可以得知,當 finally 中含有 return 的時候,會覆蓋之前 try 或者 catch(上面沒有貼出實驗,需要驗證的可以去驗證,作者本人是驗證過了) 裏面的返回值的

try, catch, finally 均沒有 return 語句

這種情況比較簡單,跟 finally 含有 return 類似

    public static int test() { 
        int i = 1;
        try { 
            i++;
        } finally { 
            i++;
            System.out.println("finally yeah!");
        }
        return i;
    }

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

總結

這裏總結一下,只要 finally 裏面沒有 return 語句,那麼返回值就由 try 或者 catch 的 return 語句決定,否則由 finally 的 return 語句決定

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/xU74zHu_vwGKzd30S0wHlQ