我在程式中使用TarArchiveOutputStream對多個檔案進行打包,finally語句塊中調用了它的close方法關閉輸出流。昨天碰到了一個很詭異的問題,執行到finally語句塊抛出異常。按正常處理邏輯,檔案打包會正常完成,finally中是不會出現異常的。然後就開始定位為什麼會出現這樣的異常,最終發現是因為打包過程中進行了額外的檢查,如果條件不符合就抛出異常,然後進入到finally語句塊。因為打包被異常中斷,導緻close出現異常,而finally中的異常會覆寫try語句中的異常,這就是我們定位這個檔案花了較長時間的原因。我們用一個簡單的測試代碼來重制和說明這個現象: 1. public class Test { 2. public static void main(String[] args) { 3. int x = 1; 4. int y = 0; 5. int z; 6. try { 7. z = (100 * x) / y; 8. System.out.println("z=" + z); 9. } finally { 10. z = x / y; 11. System.out.println("z=" + z); 12. } 13. } 14. } 可以看到第7行和第10行都會出現被0除的異常。 執行以上代碼的輸出結果: Exception in thread "main" java.lang.ArithmeticException: / by zero at Test.main(Test.java:10) 可以看到沒有列印出第7行抛出異常時的調用堆棧。這是為什麼呢? 第7行抛出異常後,進入第9行開始的finally語句塊,finally語句塊中的第10行也出現了異常,導緻前面的異常被覆寫掉,是以最後列印出的異常調用堆棧是第10行的。 解決方法,捕獲finally中可能出現的異常,修改後代碼為: 1. public class Test { 2. public static void main(String[] args) { 3. int x = 1; 4. int y = 0; 5. int z; 6. try { 7. z = (100 * x) / y; 8. System.out.println("z=" + z); 9. } finally { 10 try { 11. z = x / y; 12. System.out.println("z=" + z); 13. } catch(Exception ex) { 14. ex.printStackTrace(); 15. } 16. } 17. } 18. }
執行時的輸出為: java.lang.ArithmeticException: / by zero at Test.main(Test.java:11) Exception in thread "main" java.lang.ArithmeticException: / by zero at Test.main(Test.java:7) 可見,這時try語句中的異常資訊也出來了。
總結一下,finally中的語句,如果可能出現異常,一定要catch。 另外一個規範的做法,不要在finally中return一個傳回值。具體原因是什麼,請各位仔細思考一下,相信會想明白的