學(xué)習(xí)Java之異常到底該如何捕獲和處理
一. 捕獲和處理異常
1. 概述
在Java中,如果某行或某幾行代碼有可能會拋出異常,我們此時(shí)就可以用try ... catch ... finally進(jìn)行捕獲處理。把可能發(fā)生異常的語句放在try { ... }語句中,然后使用catch語句捕獲對應(yīng)的Exception及其子類,把必須執(zhí)行的代碼放在finally語句中。接下來我們就來看看具體的代碼實(shí)現(xiàn)吧。
2. try-catch結(jié)構(gòu)
2.1 基本語法
首先我們來看看try-catch的基本語法:
try { // 可能發(fā)生異常的語句 } catch(Exception e) { // 處理異常語句 }
在上面的語法中,我們要把可能引發(fā)異常的語句封裝在try語句塊中,用于捕獲可能發(fā)生的異常。catch語句里的( )中,要傳入與try語句里匹配的異常類,指明catch語句可以處理的異常類型。
也就是說,如果此時(shí)try語句塊中發(fā)生了某個(gè)異常,那么這個(gè)相應(yīng)的異常對象就會被拋出,產(chǎn)生異常的這行代碼之后的其余代碼就不會再被執(zhí)行。然后catch語句就開始執(zhí)行,把try中拋出的異常對象進(jìn)行捕獲并處理。
當(dāng)然,如果try語句塊中沒有發(fā)生異常,那么try里的代碼就會正常執(zhí)行結(jié)束,而后面的catch就會被跳過。
這里我們需要注意:try...catch與if...else是不一樣的,try后面的花括號{ }不可以省略,即便try中只有一行代碼。同樣的,catch的花括號 { } 也不可以省略。另外,try語句塊中聲明的變量屬于局部變量,它只在try中有效,其它地方不能訪問該變量。
2.2 代碼實(shí)現(xiàn)
接下來設(shè)計(jì)一個(gè)代碼案例,來講解try-catch的用法:
/** * @author */ public class Demo01 { public static void main(String[] args) { //定義一個(gè)長度為3的數(shù)組 int[] array = new int[3]; try { //索引超出了數(shù)組長度,將會引發(fā)ArrayIndexOutOfBoundsException數(shù)組下標(biāo)越界異常 array[3] = 1; //發(fā)生異常后,這行代碼并不會執(zhí)行 System.out.println("數(shù)組:" + array.toString()); } catch (ArrayIndexOutOfBoundsException e) { //指出異常的類型、性質(zhì)、棧層次及出現(xiàn)在程序中的位置 e.printStackTrace(); //輸出錯(cuò)誤的原因及性質(zhì) System.out.println("數(shù)組越界:" + e.getMessage()); //輸出異常的類型與性質(zhì) System.out.println("數(shù)組越界:" + e.toString()); } } }
在上面這段代碼中,小編在try語句中創(chuàng)建了一個(gè)長度為3的整數(shù)數(shù)組,并嘗試著將第4個(gè)位置上的元素值設(shè)為1。由于數(shù)組越界,這會引發(fā)代碼故障,java會拋出一個(gè)ArrayIndexOutOfBoundsException異常。由于發(fā)生了異常,所以后面的數(shù)組輸出語句就不會被執(zhí)行。
而我們在catch中接收了ArrayIndexOutOfBoundsException異常,這個(gè)異常與try中拋出的異常是一致的,所以代碼會跳轉(zhuǎn)到catch中進(jìn)行異常的處理。另外在上面的處理代碼塊中,我們可以通過Exception異常對象的以下方法,來獲取到相應(yīng)的異常信息。
- printStackTrace()方法:輸出異常棧信息,指出異常的類型、性質(zhì)、棧層次及出現(xiàn)在程序中的位置;
- getMessage()方法:輸出錯(cuò)誤的性質(zhì);
- toString()方法:輸出異常的類型與性質(zhì)。
3. 多重catch結(jié)構(gòu)
我們在編寫java代碼時(shí),多行語句有可能會產(chǎn)生多個(gè)不同的異常,你們面對這個(gè)多個(gè)異常該怎么處理呢?其實(shí)我們可以使用多重catch語句來分別處理多個(gè)異常。
3.1 基本語法
多重catch語句的基本語法格式如下:
try { // 可能會發(fā)生異常的語句 } catch(ExceptionType e) { // 處理異常語句 } catch(ExceptionType e) { // 處理異常語句 } catch(ExceptionType e) { // 處理異常語句 ... }
當(dāng)存在多個(gè)catch代碼塊時(shí),只要有一個(gè)catch代碼塊捕獲到一個(gè)異常,其它的catch代碼塊就不再進(jìn)行匹配。但是我們要注意,當(dāng)捕獲的多個(gè)異常類之間存在父子關(guān)系時(shí),一般是先捕獲子類,再捕獲父類。所以我們在編寫代碼時(shí),要把子類異常放在父類異常的前面,否則子類就會捕獲不到。
3.2 代碼實(shí)現(xiàn)
接下來給大家設(shè)計(jì)了一個(gè)利用IO流讀取文件內(nèi)容的案例,這段代碼就可能會出現(xiàn)兩個(gè)異常。
import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; /** * @author 一一哥Sun */ public class Demo02 { //多重catch語句 public static void main(String[] args) { //定義一個(gè)緩沖流對象,以后在IO流階段壹哥會細(xì)講 BufferedReader reader = null; try { //對接一個(gè)file.txt文件,該文件可能不存在 reader = new BufferedReader(new FileReader("file.txt")); //讀取文件中的內(nèi)容。所有的IO流都可能會產(chǎn)生IO流異常 String line = reader.readLine(); while (line != null) { System.out.println(line); line = reader.readLine(); } } catch (FileNotFoundException e) { //處理文件不存在時(shí)的異常 System.out.println("文件不存在:" + e.getMessage()); } catch (IOException e) { //處理IO異常 System.out.println("讀取文件失?。? + e.getMessage()); } } }
在這段代碼中,我們嘗試打開一個(gè)名為file.txt的文件,并逐行讀取其內(nèi)容。如果該文件不存在或讀取失敗,程序?qū)谙鄳?yīng)的catch塊中處理異常,并打印出異常消息。具體地來說,就是try代碼塊的第16行代碼調(diào)用了FileReader的構(gòu)造方法,這里有可能會發(fā)生FileNotFoundException異常。而第18行調(diào)用BufferedReader輸入流的readLine()方法時(shí),有可能會發(fā)生IOException異常。
因?yàn)镕ileNotFoundException異常是IOException異常的子類,所以我們應(yīng)該先捕獲 FileNotFoundException異常,即第23行代碼;后捕獲IOException異常,即第26行代碼。但是如果我們將FileNotFoundException和IOException異常的捕獲順序進(jìn)行調(diào)換,那么捕獲FileNotFoundException異常的代碼塊永遠(yuǎn)也不會執(zhí)行,所以FileNotFoundException異常也永遠(yuǎn)不會被處理。當(dāng)然,如果多個(gè)異常之間沒有父子關(guān)系,則其順序就無所謂了。
4. try-catch-finally結(jié)構(gòu)
在上面的代碼中,我們涉及到了IO流的相關(guān)內(nèi)容,這一塊我們還沒有開始進(jìn)行學(xué)習(xí),會在以后給大家進(jìn)行詳細(xì)地講解。
IO流屬于一種比較消耗物理資源的API,使用完之后應(yīng)該把IO流進(jìn)行關(guān)閉,否則就可能會導(dǎo)致物理資源被過多的消耗。那么我們該在哪里關(guān)閉IO流呢?有的人會說,我們可以在try語句塊執(zhí)行完正常的功能后關(guān)閉IO流。但是大家要知道,try語句塊和catch語句塊有可能因?yàn)楫惓2]有被完全執(zhí)行,那么try里打開的這些物理資源到底要在哪里回收呢?
為了確保這些物理資源一定可以被回收,異常處理機(jī)制給我們提供了finally代碼塊,且Java 7之后又提供了自動(dòng)資源管理(Automatic Resource Management)技術(shù),更是可以優(yōu)雅地解決資源回收的問題。
4.1 基本語法
無論是否發(fā)生異常,finally里的代碼總會被執(zhí)行。在 finally代碼塊中,我們可以執(zhí)行一些清理等收尾善后性質(zhì)的代碼,其基本語法格式如下:
try { // 可能會發(fā)生異常的語句 } catch(ExceptionType e) { // 處理異常語句 } finally { // 執(zhí)行清理代碼塊,這里的代碼肯定會被執(zhí)行到,除非極特殊的情況發(fā)生 }
上面的代碼中,無論是否發(fā)生了異常(除極特殊的情況外,比如提前調(diào)用了System.exit()退出虛擬機(jī)的方法),finally語句塊中的代碼都會被執(zhí)行。另外,finally語句也可以直接和try語句配合使用,其語法格式如下:
try { // 邏輯代碼塊 } finally { // 清理代碼塊 }
我們在使用try-catch-finally語句時(shí)要注意以下幾點(diǎn):
在異常處理的語法結(jié)構(gòu)中,只有try是必需的。如果沒有try代碼塊,則不能有后面的catch和finally;
雖然catch塊和finally塊是可選的,但也不能只有try塊,catch塊和finally塊至少要出現(xiàn)其中之一,也可以同時(shí)出現(xiàn);
可以有多個(gè)catch塊,捕獲父類異常的catch塊,必須位于捕獲子類異常的后面;
多個(gè)catch塊必須位于try塊之后,finally塊必須位于所有的catch塊之后;
只有finally與try語句塊的語法格式,這種情況會導(dǎo)致異常的丟失,所以并不常見;
通常情況下,我們不應(yīng)該在finally代碼塊中使用return或throw等會導(dǎo)致方法終止的語句,否則這將會導(dǎo)致try和catch代碼塊中的return和throw語句失效。
尤其是try-catch-finally與return的結(jié)合,是我們面試時(shí)的一個(gè)考點(diǎn)哦。有些面試官會賤賤地問你try-catch-finally中如果有return,會發(fā)生什么,請大家自行做個(gè)實(shí)驗(yàn)吧。
4.2 代碼實(shí)現(xiàn)
接下來在之前的案例基礎(chǔ)上進(jìn)行改造,引入finally代碼塊:
import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; /** * @author */ public class Demo03 { //try-catch-finally語句 public static void main(String[] args) { //定義一個(gè)緩沖流對象,以后在IO流階段壹哥會細(xì)講 BufferedReader reader = null; try { //對接一個(gè)file.txt文件,該文件可能不存在 reader = new BufferedReader(new FileReader("file.txt")); //讀取文件中的內(nèi)容。所有的IO流都可能會產(chǎn)生IO流異常 String line = reader.readLine(); while (line != null) { System.out.println(line); line = reader.readLine(); } } catch (FileNotFoundException e) { //處理文件不存在時(shí)的異常 System.out.println("文件不存在:" + e.getMessage()); } catch (IOException e) { //處理IO異常 System.out.println("讀取文件失?。? + e.getMessage()); } finally { try { //在finally代碼塊中也可以進(jìn)行try-catch的操作 if (reader != null) { //關(guān)閉IO流 reader.close(); } } catch (IOException e) { System.out.println("關(guān)閉文件失?。? + e.getMessage()); } } } }
在這段代碼中,我們嘗試打開一個(gè)名為file.txt的文件,并逐行讀取其內(nèi)容。如果文件不存在或讀取失敗,程序?qū)⒃谙鄳?yīng)的catch塊中處理異常,并打印出異常消息。無論是否發(fā)生異常,程序都會在finally塊中關(guān)閉文件。
4.3 小結(jié)
在try-catch-finally組合的結(jié)構(gòu)中,其執(zhí)行流程如下圖所示:
根據(jù)該流程可知,try-catch-finally語句塊的執(zhí)行情況可以細(xì)分為以下幾種情況:
如果try代碼塊中沒有拋出異常,則執(zhí)行完try代碼塊后會直接執(zhí)行finally代碼塊;
如果try代碼塊中拋出了異常,并被catch子句捕捉,則終止try代碼塊的執(zhí)行,轉(zhuǎn)而執(zhí)行相匹配的 catch代碼塊,之后再執(zhí)行 finally代碼塊;
如果try代碼塊中拋出的異常沒有被任何catch子句捕獲到,將會直接執(zhí)行finally代碼塊中的語句,并把該異常傳遞給該方法的調(diào)用者;
如果在finally代碼塊中也拋出了異常,則會把該異常傳遞給該方法的調(diào)用者。
二. 結(jié)語
至此,就把今天的內(nèi)容講解完畢了,通過今天的內(nèi)容,大家要注意以下幾點(diǎn):
- try...catch與if...else是不一樣的,try后面的花括號{ }不可以省略,即便try中只有一行代碼;
- 同樣的,catch的花括號 { } 也不可以省略;
- 當(dāng)捕獲的多個(gè)異常類之間存在父子關(guān)系時(shí),一般是先捕獲子類,再捕獲父類;
- 在異常處理的語法結(jié)構(gòu)中,只有try是必需的。如果沒有try代碼塊,則不能有后面的catch和finally。
以上就是學(xué)習(xí)Java之異常到底該如何捕獲和處理的詳細(xì)內(nèi)容,更多關(guān)于Java異常捕獲和處理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
大數(shù)據(jù)Kafka:消息隊(duì)列和Kafka基本介紹
本文對消息隊(duì)列的應(yīng)用場景,優(yōu)缺點(diǎn),消息隊(duì)列的兩種方式,常見的消息隊(duì)列產(chǎn)品以及Kafka的特點(diǎn)和應(yīng)用場景做了詳細(xì)的講解,需要的朋友可以參考下,希望可以對大家有所幫助2021-08-08解決出現(xiàn) java.lang.ExceptionInInitializerError錯(cuò)誤問題
這篇文章主要介紹了解決出現(xiàn) java.lang.ExceptionInInitializerError錯(cuò)誤問題的相關(guān)資料,需要的朋友可以參考下2017-01-01

String?concat(String?str)使用小結(jié)

Java JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)(Run-Time Data Areas)

SpringBoot使用hutool-captcha實(shí)現(xiàn)驗(yàn)證碼生成與驗(yàn)證