為什麼不推薦使用try-catch-finally處理Java異常?

2021-01-18 51CTO

這篇文章是我近期看了《Effective java》一書中總結的,來自其中第九條。為了對其理解的更加透徹,因此重新分析了一下,並加入了一些其他點。

  「    本文的所有例子均在本地代碼運行完畢

       基於JDK版本1.8,運行環境eclipse

       本文類名:TryWithResources,下文的堆棧信息也以此為基礎    」

在java開發中,一些網絡連結或者是文件資源都需要程式設計師去手動調用close方法關閉,比如InputStream、OutputStream和java.sql.Connection。如果忘關了就可能造成嚴重的性能後果。而關閉的方法有很多種。比如finalizer、try-catch-finally、try-with-resources等等。

finalizer機制可以關閉,但是其執行性不可預測,還有可能造成內存洩漏,所以一般不使用,雖然java9還提出了cleaner機制代替了finalizer機制,但是其執行依然不可預測,因此選擇就落在了try-catch-finally和try-with-resources之間。

本文就是為了討論該選擇哪一種比較好,不過題目已經給出了答案肯定是try-with-resources。下面帶著這個答案去分析為什麼推薦使用try-with-resources而不是try-finally。

一、前言

在正式分析之前,我們先看一波finally的執行順序。

1、finally不是必要條件

也就是說try-catch-finally中,可以只有try-catch,也可以只有try-finally。

2、假設基於try-catch-finally:

第一:代碼沒有異常

執行順序:try執行完整->catch不執行->finally執行

第二:代碼有異常且catch進行捕獲

執行順序:try執行部分->跳轉catch捕獲處理->finally執行

第三:代碼有異常且catch不捕獲:這種情況沒有catch

執行順序:try執行部分->finally執行

從上面的執行順序可以看出,finally語句不管在哪種情況是一定會執行的。基於這個認識,現在我們再來分析。

二、try-finally的缺點

先看案例,本案例來自《Effective java》,現在要關閉資源:

static String firstLineOfFile(String path) throws IOException {          BufferedReader reader = new BufferedReader(new FileReader(path));          try {              return reader.readLine();          } finally {              reader.close();          }  } 

關閉一個資源還好,但是如果再添加第二個資源,代碼看起來就會一團糟了。

static void copy(String src, String desc) throws IOException {          InputStream in = new FileInputStream(src);          try {              OutputStream out = new FileOutputStream(desc);              byte[] bytes = new byte[1024];              int n;              try {                  while ((n = in.read(bytes)) != -1) {                      out.write(bytes, 0, n);                  }              } finally {                  out.close();             }          } finally {              in.close();          }  } 

如果需要關閉的資源不僅種類多,而且數量也很多。那代碼可就太龐大了。現在對這種方式的缺點進行一波總結:

1. 關閉的資源多事,代碼複雜

2. 對於第一個案例,如果設備出現異常,那麼那麼調用readLine就會拋出異常,同時close方法也出現異常,在這種情況下,close異常會完全抹去readLine異常。在異常堆棧軌跡中也完全沒有readLine異常的記錄。

現在來測試一邊:

基於以上原因,出現了try-with-resources。

三、try-with-resources的優勢

try-with-resources是在jdk1.7引入的,可以完美解決以上的問題。要使用這個構造的資源,必須先實現AutoCloseable接口,其中包含了單個返回void的close方法,Java類庫與第三方類庫中的許多類和接口,現在都實現或擴展了AutoCloseable接口,因此我們現在不必實現了。

既然try-with-resources能夠解決以上的問題,現在來看一下,如何解決的:

1、代碼複雜問題解決

static void copy(String src, String desc) throws IOException {          try (InputStream in = new FileInputStream(src);               OutputStream out = new FileOutputStream(desc)) {              byte[] bytes = new byte[1024];              int n;              while ((n = in.read(bytes)) != -1) {                  out.write(bytes, 0, n);              }          }  } 

可以看出這種方式代碼更加簡單,出現了錯誤,也能快速定位。

2、異常抹去問題解決

static String firstLineOfFil  (String path) throws IOException {          try (BufferedReader reader = new BufferedReader(new FileReader(path))) {              return reader.readLine();          }  } 

如果readLine和不可見的close方法都拋出異常,close方法拋出的異常就會被禁止,try-finally處理機制中我們無法看到,堆棧軌跡中也不能列印,但是try-with-resources不一樣,全部會被列印在堆棧軌跡中,並註明它們是被禁止的異常,通過編寫調用getSuppressed方法還可以訪問到它們。現在再來測試一遍。

OK,上面基本上全部分析完畢,但是此書還給出了一個更好的案例:

static String firstLineOfFile(String path, String defaultVal) {          try (BufferedReader reader = new BufferedReader(new FileReader(path))) {              return reader.readLine();          } catch (IOException e) {              return defaultVal;          }  } 

這個firstLineOfFile方法沒有拋出異常,但是如果它無法打開文件,或者無法從中讀取,就會返回一個默認值。

結論

處理必須關閉的資源時,始終要優先考慮使用try-with-resources,而不是try-finally。這樣得到的代碼將更簡潔,清晰,產生的異常也更有價值,這些也是try-finally無法做到的。

【責任編輯:

龐桂玉

TEL:(010)68476606】

點讚 0

相關焦點

  • java的try、catch、finally
    不過我們在大部分的時候是不希望因為異常中斷我們的業務流程。所以需要對可能發生的異常做一些處理,讓業務可以不被中斷,繼續做處理,或者給出一些更為友好的響應消息。這個時候就是java中的try、catch、finally大展身手的時候了。
  • 為什麼推薦使用try-with-resources代替try-finally
    比如finalizer、try-catch-finally、try-with-resources等等。finalizer機制可以關閉,但是其執行性不可預測,還有可能造成內存洩漏,所以一般不使用,雖然java9還提出了cleaner機制代替了finalizer機制,但是其執行依然不可預測,因此選擇就落在了try-catch-finally和try-with-resources之間。
  • java中的try是什麼?|java的異常處理類型
    第一次接觸try的同學估計會有怎樣的疑惑:這是啥玩意?1、try-是java異常處理類型中的一個。那麼,異常處理又是啥?>3、異常處理-是java為異常提供了統一的程序出口,並且聲明了很多異常類,對每種異常類對應的處理方法;(簡單來說就是:在程序出現錯誤時,它能讓程序繼續往我們設定好的方向運行)。
  • 面試官:當return遇到try、catch、finally時會發生什麼?
    2.try,catch,finally的作用在Java中有檢查異常和非檢查異常(運行時異常)兩種異常:運行時異常,編譯時不被檢查的異常,不需要強制捕獲,編譯也能通過,他們是RuntimeException的子類。
  • Java之異常處理
    Exception:其它因編程錯誤或偶然的外在因素導致的一般性問題,可以使用針對性的代碼進行處理。例如:空指針訪問試圖讀取不存在的文件網絡連接中斷數組角標越界異常的體系結構 * java.lang.Throwable * |-----java.lang.Error:一般不編寫針對性的代碼進行處理。
  • Java學習(十二): 異常處理
    處理錯誤特點不需要打亂程序的結構,如果沒有任何錯誤產生,那麼程序的運行不受任何影響。捕捉異常異常捕獲處理是由try、catch與finally等3個關鍵字所組成的程序塊,其語法如下所示5.2 try-catch-catchtry {    // todo ...}
  • 三道try-catch-finally題目,面試妥妥的
    try-catch-finally題目分析:1.執行 foo(0)時,不滿足 try 語句塊中的 if 語句,所以不會拋出異常,執行 finallyoutput=「34」2.執行 foo(1)時,滿足 try 中的 If 語句,拋出異常,在 catch 中進行異常處理,雖然有 return 語句,但是 finally 中的內容必須執行,也就是說要先執行了 finally 才進行 return 操作,return 後 output += 「4」將不會再執行。所以output="23".
  • JAVA異常及其異常處理方式
    異常處理關鍵字通過try、catch捕獲異常try{// 程序代碼}catch(ExceptionName e1){//Catch 塊}使用throws關鍵字聲明的方法表示此方法不處理異常,而交給方法調用處進行處理。throw關鍵字拋出異常throw關鍵字作用是拋出一個異常,拋出的時候是拋出的是一個異常類的實例化對象,在異常處理中,try語句要捕獲的是一個異常對象,那麼此異常對象也可以自己拋出。
  • java安全編碼指南之:異常處理
    RuntimeException叫做運行時異常,是不需要被顯示catch住的,所以也叫做unchecked Exception。而其他非RuntimeException的Exception則需要顯示try catch,所以也叫做checked Exception。
  • 字節碼層面理解try、catch、finally
    面試中經常有關於try、catch、finally相關的問題,今天從字節碼層面了解他們的運行流程。這樣我們就能回答面試的那個問題了,因為在finally中修改了數組的值!異常處理先看看這個方法的異常表,如下圖:異常表中除了我們代碼中指明的三個異常外,編譯器還自動生成了5個Any類型的異常,用於處理其他不可預期的異常處理。
  • Java基礎之異常處理機制
    :當前方法不處理,而是聲明拋出,由該方法的調用者來處理;try-catch:在當前方法中使用try-catch的語句塊來處理異常;捕獲異常 try-catch所以可以對上述出現異常的代碼使用try-catch來處理:
  • Java提高篇——Java中的異常處理(絕對詳細,建議收藏)
    它主要用於回收在try塊裡打開的物理資源(如資料庫連接、網絡連接和磁碟文件)。只有finally塊,執行完成之後,才會回來執行try或者catch塊中的return或者throw語句,如果finally中使用了return或者throw等終止方法的語句,則就不會跳回執行,直接停止。throw -- 用於拋出異常。
  • 跟我學java編程—使用try和catch捕獲異常
    前面了解了Java異常和異常處理類,本節講述如何使用try和catch語句捕獲異常。在java中添加捕獲異常代碼時,需要把認為可能會出現異常的代碼包括在try語句塊內,處理異常的代碼包括在catch語句內。在程序執行時,如果try語句內的代碼出現錯誤,try會創建異常對象並拋出,catch捕獲異常對象,則catch語句塊內的代碼將會執行,這樣就可以處理異常錯誤了。
  • Java面試題:try-catch-finally中return的用法
    結果如下:執行結果從結果分析看,catch中的return前執行了finally,並且在執行finally前,return總結如下:finally塊的語句在try或catch中的return語句執行之後返回之前執行,若finally裡也有return語句則覆蓋try或catch中的return語句直接返回;若finally中沒有return則返回try或者catch中的已確定的return值。
  • 處理Java異常的9個最佳實踐
    這是一個令初學者流淚,令老手聞之汗顏的話題,因為即使是身經百戰的開發者也可能花上幾個小時來討論哪些異常應該拋出或處理,以及如何拋出或處理異常。這就是為什麼大多數開發團隊都有自己的一套規則。如果你剛進入一個新的團隊,那麼你可能會驚訝於這些規則與以前使用過的規則是如此的大相逕庭。
  • Java中最正確的異常處理方式
    >受檢異常是編譯器要求必須處理的異常,必須使用 try catch 處理,或者使用 throw 拋出,交給上層調用者處理。throws 用在函數上捕獲異常通常拋出異常後,還需要將異常捕獲。使用 try 和 catch 語句塊來捕獲異常,有時候還會用到 finally。對於上述三個關鍵詞所構成的語句塊,try 語句塊是必不可少的,catch 和 finally 語句塊可以根據情況選擇其一或者全選。
  • 世上最真情的愛戀就是你在try我在catch(這我酸了……)
    ,世上最真情的愛戀就是你在try我在catch,無論你發什麼脾氣,我都靜靜的接受,默默地處理,不管你有什麼錯,我都會原諒你,愛著你。這類異常在編譯時就必須做出處理,否則無法通過編譯處理這類異常有兩種方式:使用try—catch捕獲異常(java的神仙cp出場啦),使用throws聲明異常try—catch—finally我們可以先想一下地鐵的安保機制,是不是乘地鐵的人進去是不是要過一下安檢
  • Java異常之異常處理機制
    異常處理機制 拉勾IT課小編為大家分解 1、拋出異常2、捕獲異常3、異常處理五個關鍵字:try、catch、finally、throw、throws注意:假設要捕獲多個異常:需要按照層級關係(異常體系結構) 從小到大!
  • 最最最全面的Java異常面試及解答
    Java異常機制可以使程序中異常處理代碼和正常業務代碼分離,保證程序代碼更加優雅,並提高程序健壯性。在有效使用異常的情況下,異常能清晰的回答what, where, why這3個問題:異常類型回答了「什麼」被拋出,異常堆棧跟蹤回答了「在哪」拋出,異常信息回答了「為什麼」會拋出。
  • JAVA語言如何進行異常處理,在try塊中可以拋出異常嗎?
    Java通過面向對象的方法進行異常處理,把各種不同的異常進行分類,並提供了良好的接口。在Java中,每個異常都是一個對象,它是Throwable類或其它子類的實例。當一個方法出現異常後便拋出一個異常對象,該對象中包含有異常信息,調用這個對象的方法可以捕獲到這個異常並進行處理。