捕獲和處理異常
本節描述如何使用三個異常處理程序組件 — try、catch和finally塊 — 來編寫異常處理程序,然后,解釋了Java SE 7中引入的try-with-resources語句,try-with-resources語句特別適用于使用Closeable資源的情況,例如流。
本節的最后一部分將介紹一個示例,并分析各種場景中發生的情況。
以下示例定義并實現名為ListOfNumbers的類,構造時,ListOfNumbers創建一個ArrayList,其中包含10個具有順序值0到9的整數元素,ListOfNumbers類還定義了一個名為writeList的方法,該方法將數字列表寫入名為OutFile.txt的文本文件中,此示例使用java.io中定義的輸出類,這些類在基礎I/O中介紹。
// Note: This class will not compile yet. import java.io.*; import java.util.List; import java.util.ArrayList; public class ListOfNumbers { private Listlist; private static final int SIZE = 10; public ListOfNumbers () { list = new ArrayList (SIZE); for (int i = 0; i < SIZE; i++) { list.add(new Integer(i)); } } public void writeList() { // The FileWriter constructor throws IOException, which must be caught. PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt")); for (int i = 0; i < SIZE; i++) { // The get(int) method throws IndexOutOfBoundsException, which must be caught. out.println("Value at: " + i + " = " + list.get(i)); } out.close(); } }
FileWriter構造函數初始化文件上的輸出流,如果無法打開該文件,則構造函數將拋出IOException。對ArrayList類的get方法的調用,如果其參數的值太?。ㄐ∮?)或太大(大于ArrayList當前包含的元素數),則拋出IndexOutOfBoundsException。
如果你嘗試編譯ListOfNumbers類,編譯器將輸出有關FileWriter構造函數拋出的異常的錯誤消息,但是,它不會顯示有關get拋出的異常的錯誤消息,原因是構造函數拋出的異常IOException是一個經過檢查的異常,而get方法拋出的異常(IndexOutOfBoundsException)是一個未經檢查的異常。
現在你已熟悉ListOfNumbers類以及可以在其中拋出異常的位置,你已準備好編寫異常處理程序來捕獲和處理這些異常。
try塊構造異常處理程序的第一步是使用try塊將可能引發異常的代碼括起來,通常,try塊如下所示:
try { code } catch and finally blocks . . .
標記為code的示例段中包含一個或多個可能引發異常的合法代碼行(catch和finally塊將在接下來的兩個小節中解釋)。
要從ListOfNumbers類構造writeList方法的異常處理程序,請將writeList方法的異常拋出語句包含在try塊中,有不止一種方法可以做到這一點,你可以將可能引發異常的每行代碼放在其自己的try塊中,并為每個代碼提供多帶帶的異常處理程序。或者,你可以將所有writeList代碼放在一個try塊中,并將多個處理程序與它相關聯,以下列表對整個方法使用一個try塊,因為所討論的代碼非常短。
private Listlist; private static final int SIZE = 10; public void writeList() { PrintWriter out = null; try { System.out.println("Entered try statement"); out = new PrintWriter(new FileWriter("OutFile.txt")); for (int i = 0; i < SIZE; i++) { out.println("Value at: " + i + " = " + list.get(i)); } } catch and finally blocks . . . }
如果try塊中發生異常,則該異常由與之關聯的異常處理程序處理,要將異常處理程序與try塊關聯,必須在其后面放置一個catch塊。
catch塊通過在try塊之后直接提供一個或多個catch塊,可以將異常處理程序與try塊關聯,try塊的末尾和第一個catch塊的開頭之間不能有代碼。
try { } catch (ExceptionType name) { } catch (ExceptionType name) { }
每個catch塊都是一個異常處理程序,它處理由其參數表示的異常類型,參數類型ExceptionType聲明了處理程序可以處理的異常類型,并且必須是從Throwable類繼承的類的名稱,處理程序可以使用name引用異常。
catch塊包含在調用異常處理程序時執行的代碼,當處理程序是調用堆棧中的第一個ExceptionType與拋出的異常類型匹配時,運行時系統調用此異常處理程序,如果拋出的對象可以合法地分配給異常處理程序的參數,則系統認為它是匹配的。
以下是writeList方法的兩個異常處理程序:
try { } catch (IndexOutOfBoundsException e) { System.err.println("IndexOutOfBoundsException: " + e.getMessage()); } catch (IOException e) { System.err.println("Caught IOException: " + e.getMessage()); }
異常處理程序不僅可以打印錯誤消息或停止程序,它們可以執行錯誤恢復、提示用戶做出決定,或使用鏈式異常將錯誤傳播到更高級別的處理程序,如“鏈式異?!辈糠炙觥?/p> 使用一個異常處理程序捕獲多種類型的異常
在Java SE 7及更高版本中,單個catch塊可以處理多種類型的異常,此功能可以減少代碼重復并減少捕獲過于寬泛的異常的誘惑。
在catch子句中,指定塊可以處理的異常類型,并使用豎線(|)分隔每個異常類型:
catch (IOException|SQLException ex) { logger.log(ex); throw ex; }
注意:如果catch塊處理多個異常類型,則catch參數隱式為final,在此示例中,catch參數ex是final,因此你無法在catch塊中為其分配任何值。finally塊
當try塊退出時,finally塊總是執行,這確保即使發生意外異常也會執行finally塊,但finally不僅僅是異常處理有用 — 它允許程序員避免因return、continue或break而意外繞過清理代碼,將清理代碼放在finally塊中始終是一種很好的做法,即使沒有預期的異常情況也是如此。
注意:如果在執行try或catch代碼時JVM退出,則finally塊可能無法執行,同樣,如果執行try或catch代碼的線程被中斷或終止,則即使應用程序作為一個整體繼續,finally塊也可能無法執行。
你在此處使用的writeList方法的try塊打開了PrintWriter,程序應該在退出writeList方法之前關閉該流,這帶來了一個有點復雜的問題,因為writeList的try塊可以以三種方式之一退出。
new FileWriter語句失敗并拋出IOException。
list.get(i)語句失敗并拋出IndexOutOfBoundsException。
一切都成功,try塊正常退出。
無論try塊中發生了什么,運行時系統總是執行finally塊中的語句,所以這是進行清理的最佳地點。
下面的writeList方法的finally塊清理然后關閉PrintWriter。
finally { if (out != null) { System.out.println("Closing PrintWriter"); out.close(); } else { System.out.println("PrintWriter not open"); } }
重要提示:finally塊是防止資源泄漏的關鍵工具,關閉文件或以其他方式恢復資源時,將代碼放在finally塊中以確保始終恢復資源。try-with-resources語句請考慮在這些情況下使用try-with-resources語句,這會在不再需要時自動釋放系統資源,try-with-resources語句部分提供了更多信息。
try-with-resources語句是一個聲明一個或多個資源的try語句,資源是在程序完成后必須關閉的對象,try-with-resources語句確保在語句結束時關閉每個資源,實現java.lang.AutoCloseable的任何對象(包括實現java.io.Closeable的所有對象)都可以用作資源。
以下示例從文件中讀取第一行,它使用BufferedReader實例從文件中讀取數據,BufferedReader是一個在程序完成后必須關閉的資源:
static String readFirstLineFromFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } }
在此示例中,try-with-resources語句中聲明的資源是BufferedReader,聲明語句出現在try關鍵字后面的括號內,Java SE 7及更高版本中的BufferedReader類實現了java.lang.AutoCloseable接口,因為BufferedReader實例是在try-with-resource語句中聲明的,所以無論try語句是正常完成還是突然完成(由于BufferedReader.readLine方法拋出IOException),它都將被關閉。
在Java SE 7之前,你可以使用finally塊來確保關閉資源,無論try語句是正常還是突然完成,以下示例使用finally塊而不是try-with-resources語句:
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { if (br != null) br.close(); } }
但是,在此示例中,如果方法readLine和close都拋出異常,則方法readFirstLineFromFileWithFinallyBlock拋出finally塊拋出的異常,從try塊拋出的異常被抑制。相反,在示例readFirstLineFromFile中,如果從try塊和try-with-resources語句拋出異常,則readFirstLineFromFile方法拋出try塊拋出的異常,從try-with-resources塊拋出的異常被抑制,在Java SE 7及更高版本中,你可以檢索已抑制的異常,有關詳細信息,請參閱“抑制的異?!辈糠?。
你可以在try-with-resources語句中聲明一個或多個資源,以下示例檢索zip文件zipFileName中打包的文件的名稱,并創建包含這些文件名稱的文本文件:
public static void writeToFileZipFileContents(String zipFileName, String outputFileName) throws java.io.IOException { java.nio.charset.Charset charset = java.nio.charset.StandardCharsets.US_ASCII; java.nio.file.Path outputFilePath = java.nio.file.Paths.get(outputFileName); // Open zip file and create output file with // try-with-resources statement try ( java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName); java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset) ) { // Enumerate each entry for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) { // Get the entry name and write it to the output file String newLine = System.getProperty("line.separator"); String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine; writer.write(zipEntryName, 0, zipEntryName.length()); } } }
在此示例中,try-with-resources語句包含兩個以分號分隔的聲明:ZipFile和BufferedWriter,當直接跟隨它的代碼塊正?;蛴捎诋惓6K止時,將按此順序自動調用BufferedWriter和ZipFile對象的close方法,請注意,資源的close方法按其創建的相反順序調用。
以下示例使用try-with-resources語句自動關閉java.sql.Statement對象:
public static void viewTable(Connection con) throws SQLException { String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES"; try (Statement stmt = con.createStatement()) { ResultSet rs = stmt.executeQuery(query); while (rs.next()) { String coffeeName = rs.getString("COF_NAME"); int supplierID = rs.getInt("SUP_ID"); float price = rs.getFloat("PRICE"); int sales = rs.getInt("SALES"); int total = rs.getInt("TOTAL"); System.out.println(coffeeName + ", " + supplierID + ", " + price + ", " + sales + ", " + total); } } catch (SQLException e) { JDBCTutorialUtilities.printSQLException(e); } }
此示例中使用的資源java.sql.Statement是JDBC 4.1及更高版本API的一部分。
注意:try-with-resources語句可以像普通的try語句一樣有catch和finally塊,在try-with-resources語句中,在聲明的資源關閉后運行任何catch或finally塊。抑制異常
可以從與try-with-resources語句關聯的代碼塊中拋出異常,在示例writeToFileZipFileContents中,可以從try塊拋出異常,當try-with-resources語句嘗試關閉ZipFile和BufferedWriter對象時,最多可以拋出兩個異常。如果從try塊拋出異常,并且從try-with-resources語句中拋出一個或多個異常,則會抑制從try-with-resources語句拋出的那些異常,并且塊拋出的異常是writeToFileZipFileContents方法拋出的異常,你可以通過從try塊拋出的異常中調用Throwable.getSuppressed方法來檢索這些抑制的異常。
實現AutoCloseable或Closeable接口的類請參閱AutoCloseable和Closeable接口的Javadoc,以獲取實現這些接口之一的類列表,Closeable接口擴展了AutoCloseable接口。Closeable接口的close方法拋出IOException類型的異常,而AutoCloseable接口的close方法拋出異常類型Exception,因此,AutoCloseable接口的子類可以覆蓋close方法的這種行為,以拋出專門的異常,例如IOException,或者根本沒有異常。
把它們放在一起前面的部分描述了如何為ListOfNumbers類中的writeList方法構造try、catch和finally代碼塊,現在,讓我們來看看代碼并調查會發生什么。
將所有組件放在一起時,writeList方法如下所示。
public void writeList() { PrintWriter out = null; try { System.out.println("Entering" + " try statement"); out = new PrintWriter(new FileWriter("OutFile.txt")); for (int i = 0; i < SIZE; i++) { out.println("Value at: " + i + " = " + list.get(i)); } } catch (IndexOutOfBoundsException e) { System.err.println("Caught IndexOutOfBoundsException: " + e.getMessage()); } catch (IOException e) { System.err.println("Caught IOException: " + e.getMessage()); } finally { if (out != null) { System.out.println("Closing PrintWriter"); out.close(); } else { System.out.println("PrintWriter not open"); } } }
如前所述,此方法的try塊有三種不同的退出可能性,這是其中兩個。
try語句中的代碼失敗并引發異常,這可能是由new FileWriter語句引起的IOException或由for循環中的錯誤索引值引起的IndexOutOfBoundsException。
一切都成功,try語句正常退出。
場景1:發生異常創建FileWriter的語句可能由于多種原因而失敗,例如,如果程序無法創建或寫入指示的文件,則FileWriter的構造函數將拋出IOException。
當FileWriter拋出IOException時,運行時系統立即停止執行try塊,正在執行的方法調用未完成,然后,運行時系統開始在方法調用堆棧的頂部搜索適當的異常處理程序。在此示例中,發生IOException時,FileWriter構造函數位于調用堆棧的頂部,但是,FileWriter構造函數沒有適當的異常處理程序,因此運行時系統在方法調用堆棧中檢查下一個方法 — writeList方法,writeList方法有兩個異常處理程序:一個用于IOException,另一個用于IndexOutOfBoundsException。
運行時系統按照它們在try語句之后出現的順序檢查writeList的處理程序,第一個異常處理程序的參數是IndexOutOfBoundsException,這與拋出的異常類型不匹配,因此運行時系統會檢查下一個異常處理程序 — IOException,這與拋出的異常類型相匹配,因此運行時系統結束搜索適當的異常處理程序,既然運行時已找到適當的處理程序,那么執行該catch塊中的代碼。
異常處理程序執行后,運行時系統將控制權傳遞給finally塊,無論上面捕獲的異常如何,finally塊中的代碼都會執行,在這種情況下,FileWriter從未打開過,不需要關閉,在finally塊完成執行后,程序繼續執行finally塊之后的第一個語句。
這是拋出IOException時出現的ListOfNumbers程序的完整輸出。
Entering try statement Caught IOException: OutFile.txt PrintWriter not open
以下清單中的后加*號的代碼顯示了在此方案中執行的語句:
public void writeList() { PrintWriter out = null; //****** try { System.out.println("Entering try statement"); //******* out = new PrintWriter(new FileWriter("OutFile.txt")); //****** for (int i = 0; i < SIZE; i++) out.println("Value at: " + i + " = " + list.get(i)); } catch (IndexOutOfBoundsException e) { System.err.println("Caught IndexOutOfBoundsException: " + e.getMessage()); } catch (IOException e) { System.err.println("Caught IOException: " + e.getMessage()); //***** } finally { if (out != null) { //***** System.out.println("Closing PrintWriter"); out.close(); } else { System.out.println("PrintWriter not open"); /****** } } }場景2:try塊正常退出
在這種情況下,try塊范圍內的所有語句都成功執行,并且不會拋出異常,執行在try塊的末尾結束,運行時系統將控制傳遞給finally塊,因為一切都成功了,所以當控制到達finally塊時,PrintWriter會打開,這會關閉PrintWriter,同樣,在finally塊完成執行之后,程序繼續執行finally塊之后的第一個語句。
當沒有拋出異常時,這是ListOfNumbers程序的輸出。
Entering try statement Closing PrintWriter
以下示例中的加*號的代碼顯示了在此方案中執行的語句。
public void writeList() { PrintWriter out = null; //***** try { System.out.println("Entering try statement"); //****** out = new PrintWriter(new FileWriter("OutFile.txt")); //****** for (int i = 0; i < SIZE; i++) //****** out.println("Value at: " + i + " = " + list.get(i)); //****** } catch (IndexOutOfBoundsException e) { System.err.println("Caught IndexOutOfBoundsException: " + e.getMessage()); } catch (IOException e) { System.err.println("Caught IOException: " + e.getMessage()); } finally { if (out != null) { //****** System.out.println("Closing PrintWriter"); //****** out.close(); //******* } else { System.out.println("PrintWriter not open"); } } }上一篇:捕獲或指定要求 下一篇:如何拋出異常
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77482.html
捕獲或指定要求 有效的Java編程語言代碼必須遵守捕獲或指定需求,這意味著可能拋出某些異常的代碼必須包含以下任一項: 捕獲異常的try語句,try必須為異常提供處理程序,如捕獲和處理異常中所述。 一種方法,指定它可以拋出異常,該方法必須提供一個throws子句,列出異常,如通過方法拋出指定異常中所述。 不符合捕獲或指定要求的代碼將無法編譯。 并非所有異常都受捕獲或指定要求的約束,為了理解原因,...
異常的優點 現在你已經知道了什么是異常以及如何使用它們,現在是時候了解在程序中使用異常的優勢了。 優點1:將錯誤處理代碼與常規代碼分開 異常提供了從程序的主邏輯中分離異常發生時應該做什么的細節的方法,在傳統的編程中,錯誤檢測、報告和處理通常會導致混亂的意大利面代碼,例如,考慮這里的偽代碼方法將整個文件讀入內存。 readFile { open the file; determine...
如何拋出異常 在捕獲異常之前,某些代碼必須拋出一個,任何代碼都可能拋出異常:你的代碼,來自其他人編寫的包中的代碼,例如Java平臺附帶的包或Java運行時環境,無論拋出什么異常,它總是使用throw語句拋出。 你可能已經注意到,Java平臺提供了許多異常類,所有類都是Throwable類的后代,并且所有類都允許程序區分在程序執行期間可能發生的各種類型的異常。 你還可以創建自己的異常類來表示你編寫的...
什么是異常? exception一詞是exceptional event這一短語的簡寫。 定義:異常是在程序執行期間發生的事件,它會破壞程序指令的正常流程。 當方法中發生錯誤時,該方法會創建一個對象并將其交給運行時系統,該對象稱為異常對象,包含有關錯誤的信息,包括錯誤發生時的類型和程序狀態,創建異常對象并將其交給運行時系統稱為拋出異常。 在方法拋出異常后,運行時系統會嘗試查找處理它的內容,處理異常...
Java? 教程 Java教程是為JDK 8編寫的,本頁面中描述的示例和實踐沒有利用在后續版本中引入的改進。 Java教程是希望使用Java編程語言創建應用程序的程序員的實用指南,其中包括數百個完整的工作示例和數十個課程,相關課程組被組織成教程。 覆蓋基礎知識的路徑 這些教程以書籍的形式提供,如Java教程,第六版,前往Amazon.com購買。 入門 介紹Java技術和安裝Java開發軟件并使用...
閱讀 2211·2021-11-22 13:54
閱讀 3376·2019-08-29 12:25
閱讀 3440·2019-08-28 18:29
閱讀 3579·2019-08-26 13:40
閱讀 3275·2019-08-26 13:32
閱讀 955·2019-08-26 11:44
閱讀 2229·2019-08-23 17:04
閱讀 2968·2019-08-23 17:02