在本教程中,您將借助示例學習如何用Java處理異常。為了處理異常,我們將使用try ... catch ... finally塊。
在上一教程中,我們了解了異常。異常是程序執(zhí)行期間發(fā)生的意外事件。
在Java中,我們使用異常處理程序組件try,catch和finally塊來處理異常。
為了捕獲和處理異常,我們將try...catch...finally代碼塊放置在可能產(chǎn)生異常的代碼周圍。finally塊是可選的。
try...catch...finally的語法為:
try { // 代碼 } catch (ExceptionType e) { // 捕獲塊 } finally { //finally塊 }
可能會生成異常的代碼放在try塊中。
每個try塊后面應緊跟著catch 或 finally塊。發(fā)生異常時,它會被catch緊隨其后的塊捕獲。
catch塊不能單獨使用,必須緊隨try塊。
class Main { public static void main(String[] args) { try { int divideByZero = 5 / 0; System.out.println("try塊中的其余代碼"); } catch (ArithmeticException e) { System.out.println("ArithmeticException => " + e.getMessage()); } } }
輸出結果
ArithmeticException => / by zero
在這個實例中
我們在try塊中將數(shù)字除以0。這產(chǎn)生一個ArithmeticException。
發(fā)生異常時,程序將跳過try塊中的其余代碼。
在這里,我們創(chuàng)建了一個catch塊來處理ArithmeticException。因此,將catch執(zhí)行塊內(nèi)的語句。
如果該try塊中的所有語句均未生成異常,則跳過catch代碼塊。
對于每個try塊,可以有零個或多個catch塊。
每個catch塊的參數(shù)類型指示可以處理的異常類型。多個catch塊使我們能夠以不同方式處理每個異常。
class ListOfNumbers { public int[] arrayOfNumbers = new int[10]; public void writeList() { try { arrayOfNumbers[10] = 11; } catch (NumberFormatException e1) { System.out.println("NumberFormatException => " + e1.getMessage()); } catch (IndexOutOfBoundsException e2) { System.out.println("IndexOutOfBoundsException => " + e2.getMessage()); } } } class Main { public static void main(String[] args) { ListOfNumbers list = new ListOfNumbers(); list.writeList(); } }
輸出結果
IndexOutOfBoundsException => Index 10 out of bounds for length 10
在此示例中,我們聲明了一個大小為10 的整數(shù)數(shù)組arrayOfNumbers。
我們知道數(shù)組索引總是從0開始。因此,當我們嘗試為索引10分配一個值時,就會發(fā)生IndexOutOfBoundsException,因為數(shù)組arrayOfNumbers的邊界是0到9。
當try塊中發(fā)生異常時,
異常被拋出給第一個catch塊。第一個catch塊不處理IndexOutOfBoundsException異常,因此它被傳遞給下一個catch塊。
上面示例中的第二個catch塊是適當?shù)漠惓L幚沓绦颍驗樗幚鞩ndexOutOfBoundsException。 因此,它被執(zhí)行。
對于每個try塊,只能有一個finally塊。
finally塊是可選的。但是,如果已定義,它將始終執(zhí)行(即使不會發(fā)生異常)。
如果發(fā)生異常,則在try...catch塊之后執(zhí)行。如果沒有異常發(fā)生,則在try塊之后執(zhí)行。
finally塊的基本語法為:
try { //code } catch (ExceptionType1 e1) { // catch 塊 } catch (ExceptionType1 e2) { // catch 塊 } finally { //finally塊一直執(zhí)行 }
class Main { public static void main(String[] args) { try { int divideByZero = 5 / 0; } catch (ArithmeticException e) { System.out.println("ArithmeticException => " + e.getMessage()); } finally { System.out.println("Finally塊總是執(zhí)行"); } } }
輸出結果
ArithmeticException => / by zero Finally塊總是執(zhí)行
在此示例中,我們將數(shù)字除以0。這引發(fā)了一個ArithmeticException被catch塊捕獲,finally塊始終執(zhí)行。
使用finally塊被認為是一種很好的做法。這是因為它包含了重要的清理代碼,例如
可能被return、continue或break語句意外跳過的代碼
關閉文件或連接
我們已經(jīng)提到,finally總是執(zhí)行,通常是這樣的。但是,在某些情況下,finally塊不執(zhí)行:
使用 System.exit()方法
finally塊中發(fā)生異常
線程被終止
讓我們舉一個實例,我們嘗試使用FileWriter創(chuàng)建一個新文件,并使用PrintWriter寫入數(shù)據(jù)。
import java.io.*; class ListOfNumbers { private int[] list = new int[10]; public ListOfNumbers() { //在列表數(shù)組中存儲整數(shù)值 for (int i = 0; i < 10; i++) { list[i] = i; } } } public void writeList() { PrintWriter out = null; try { System.out.println("進入try語句"); //創(chuàng)建一個新文件OutputFile.txt out = new PrintWriter(new FileWriter("OutputFile.txt")); //將值從列表數(shù)組寫入新創(chuàng)建的文件 for (int i = 0; i < 10; i++) { out.println("Value at: " + i + " = " + list[i]); } } catch (IndexOutOfBoundsException e1) { System.out.println("IndexOutOfBoundsException => " + e1.getMessage()); } catch (IOException e2) { System.out.println("IOException => " + e2.getMessage()); } finally { //檢查PrintWriter是否被打開 if (out != null) { System.out.println("關閉PrintWriter"); out.close(); } else { System.out.println("PrintWriter無法打開"); } } } } class Main { public static void main(String[] args) { ListOfNumbers list = new ListOfNumbers(); list.writeList(); } }
當您運行此程序時,可能會發(fā)生兩種可能性:
try塊中發(fā)生異常
try塊正常執(zhí)行
創(chuàng)建新的FileWriter時可能會發(fā)生異常。 如果無法創(chuàng)建或寫入指定的文件,則拋出IOException。
當發(fā)生異常時,我們將獲得以下輸出。
進入try語句 IOException => OutputFile.txt PrintWriter無法打開
當未發(fā)生異常且該try塊正常執(zhí)行時,我們將獲得以下輸出。
進入try語句 關閉PrintWriter
將創(chuàng)建一個OutputFile.txt,并包含以下內(nèi)容
Value at: 0 = 0 Value at: 1 = 1 Value at: 2 = 2 Value at: 3 = 3 Value at: 4 = 4 Value at: 5 = 5 Value at: 6 = 6 Value at: 7 = 7 Value at: 8 = 8 Value at: 9 = 9
讓我們嘗試在上述示例的幫助下詳細了解異常處理的流程。
上圖描述了在創(chuàng)建新FileWriter時發(fā)生異常時的程序執(zhí)行流程。
為了找到發(fā)生異常的方法,主方法調(diào)用writeList()方法,該方法隨后調(diào)用FileWriter()方法來創(chuàng)建一個新的OutputFile.txt文件。
發(fā)生異常時,運行時系統(tǒng)將跳過try塊中的其余代碼。
它開始以相反的順序搜索調(diào)用堆棧,以找到合適的異常處理程序。
這里,F(xiàn)ileWriter沒有異常處理程序,因此運行時系統(tǒng)檢查調(diào)用堆棧中的下一個方法,即writeList。
writeList方法有兩個異常處理程序:一個處理IndexOutOfBoundsException,另一個處理IOException。
然后,系統(tǒng)依次處理這些處理程序。
此示例中的第一個處理程序處理IndexOutOfBoundsException。 這與try塊引發(fā)的IOException不匹配。
因此,檢查下一個處理程序是哪個IOException處理程序。如與引發(fā)的異常類型匹配,因此將執(zhí)行對應catch塊中的代碼。
執(zhí)行異常處理程序后,將執(zhí)行finally塊。
在此場景中,由于FileWriter中發(fā)生了異常,所以PrintWriter對象out從未打開,因此不需要關閉。
現(xiàn)在,讓我們假設在運行該程序時沒有發(fā)生異常,并且try塊正常執(zhí)行。 在這種情況下,將創(chuàng)建并寫入一個OutputFile.txt。
眾所周知,finally塊的執(zhí)行與異常處理無關。由于沒有異常發(fā)生,因此PrintWriter打開了并且需要關閉。這是通過finally塊中的out.close()語句完成的。
從Java SE 7和更高版本開始,我們現(xiàn)在可以用一個catch塊捕獲不止一種類型的異常。
這樣可以減少代碼重復并提高代碼的簡單性和效率。
可以由catch塊處理的每種異常類型都使用豎線(|)分隔。
其語法為:
try { // code } catch (ExceptionType1 | Exceptiontype2 ex) { // catch block }
要了解更多信息,請訪問Java捕獲多個異常。
try-with-resources語句是一種try語句,具有一個或多個資源聲明。
其語法為:
try (resource declaration) { // use of the resource } catch (ExceptionType e1) { // catch block }
資源是在程序結束時要關閉的對象。必須在try語句中聲明和初始化它。
讓我們舉個實例。
try (PrintWriter out = new PrintWriter(new FileWriter("OutputFile.txt")) { // use of the resource }
try-with-resources語句也稱為自動資源管理。該語句在語句末尾自動關閉所有資源。
要了解更多信息,請訪問Java try-with-resources語句。