異常處理方式一:在當前方法中直接用try…catch處理
異常處理方式二:在當前方法中不處理,throws 異常拋給調用者處理
1 方式1:try…catch...finally捕獲異常
1.1 try…catch代碼塊
try-catch的方式就是捕獲異常。
-
try:該代碼塊中編寫可能產生異常的代碼。
-
catch:用來進行某種異常的捕獲,對捕獲到的異常進行處理。
格式:
//形式一:一個catch塊
try{
可能發生異常的代碼(沒有異常的代碼也可以放進來)
}catch(異常類型 異常名e){
處理異常的代碼(一般都是打印異常的信息的語句)
}
//形式二:多個catch塊
try{
可能發生異常的代碼
}catch(異常類型 異常名e){
處理異常的代碼(一般都是打印異常的信息的語句)
}catch(異常類型 異常名e){
處理異常的代碼(一般都是打印異常的信息的語句)
}
...
catch(異常類型 異常名e){
處理異常的代碼(一般都是打印異常的信息的語句)
}
try 和 catch 都不能單獨使用
注意:
- catch 中定義的異常變量的類型是根據 try 中拋出的異常來定義的;一般拋出什么類型的異常對象,就定義什么類型異常變量來接收這個異常對象
- try 中可能會拋出多個異常對象,就可以定義多個 catch 來分別處理這些異常對象
- try代碼中如果出現了異常,那么就不會繼續執行 try 中的代碼,找對應的匹配的catch分支執行,執行完畢后繼續執行try...cath之后的代碼
- try代碼中如果沒有出現異常,正常執行try中的代碼,不會執行catch中異常的處理邏輯,會執行執行try...catch之后的代碼
-
多個異常該如何處理呢
- 多個異常分別處理,一個異常對應一個try一個catch
- 多個異常一次捕獲多次處理,一個try多個catch
- 多個異常一次捕獲一次處理,只用一個try一個catch,定義一個范圍更大的父類異常對象
一般我們是使用一次捕獲多次處理方式,格式如下:
try{ 編寫可能會出現異常的代碼 }catch(異常類型A e){ 處理異常的代碼 //記錄日志/打印異常信息/繼續拋出異常 }catch(異常類型B e){ 處理異常的代碼 //記錄日志/打印異常信息/繼續拋出異常 }...
注意:這種異常處理方式,要求多個catch中的異常不能相同,並且若catch中的多個異常之間有繼承關系,那么要求子類異常在上面的catch處理,父類異常在下面的catch處理;若無繼承關系,順序隨意。
演示如下:
public class ExceptionDemo {
public static void main(String[] args) {
try {
show(1);
}catch (NullPointerException e) {
System.out.println("處理空指針異常");
}catch (ArrayIndexOutOfBoundsException e) {
System.out.println("處理數組下標越界異常");
}
System.out.println("程序結束!");
}
private static void show(int a) {
if (a == 0) {
int[] arr = new int[0];
System.out.println(arr[1]);
}else {
int[] arr = null;
System.out.println(arr.length);
}
}
}
1.2 finally 代碼塊
finally:有一些特定的代碼無論異常是否發生,都需要執行。另外,因為異常會引發程序跳轉,導致有些語句執行不到。而finally就是解決這個問題的,在finally代碼塊中存放的代碼都是一定會被執行的。
什么代碼必須最終執行?
當我們在try語句塊中打開了一些物理資源(磁盤文件/網絡連接/數據庫連接等),我們都得在使用完之后關閉打開的資源。
格式:
//形式一:try...catch...finally
try{
可能發生異常的代碼
}catch(異常類型 異常名e){
處理異常的代碼(一般都是打印異常的信息的語句)
}
...
catch(異常類型 異常名e){
處理異常的代碼(一般都是打印異常的信息的語句)
}
finally{
無論try中是否有異常,也不管catch是否可以捕獲異常,也不管try和catch中是不是有return,都要執行的部分
}
//形式二:try...finally(一般不用)
try{
可能發生異常的代碼
}finally{
無論try中是否有異常,也不管是不是有return,都要執行的部分
}
注意:finally不能單獨使用
finally代碼參考如下:
public class ExceptionDemo2 {
public static void main(String[] args) {
try {
show(1);
}catch (NullPointerException e) {
System.out.println("處理空指針異常");
}catch (ArrayIndexOutOfBoundsException e) {
System.out.println("處理數組下標越界異常");
}finally {
System.out.println("finally中的方法一定會執行");
}
System.out.println("程序結束!");
}
private static void show(int a) {
if (a == 0) {
int[] arr = new int[0];
System.out.println(arr[1]);
}else {
int[] arr = null;
System.out.println(arr.length);
}
}
}
當只有在try或者catch中調用退出JVM的相關方法,finally才不會執行,否則finally永遠會執行。
finally與return混用的情況(避免使用)
- 不管try中是否發生異常,也不管catch是否可以捕獲異常,也無論try或catch中是否有return語句,finally中的代碼都必須執行
- 如果finally中有return,就從finally塊的的return返回。
- 如果finally中沒有return,那么先把try或catch中該執行的執行完(包括把返回值的結果放到要帶回調用處的操作數棧的位置)
演示代碼:
public class ExceptionDemo3 {
public static void main(String[] args) {
int num = getNum(4);
System.out.println(num); // 0
}
private static int getNum(int a){
int result = 10;
try{
System.out.println(a/0);
if(a > 0){
result = 20;
return result;
}else if(a < 0){
result = -20;
return result;
}else{
return result;
}
}catch(Exception e){
System.out.println("exception");
result = 0;
return result;
}finally{
result = 30;
System.out.println("finally");
// return result;//如果有這句,結果就變成30
}
}
}
return 值; 語句有兩個動作:
(1)把返回值放到”操作數棧“中
(2)結束當前方法的執行,把“操作數棧”中的值會返回給調用處
如果finally中沒有return,finally中的語句會執行,但是不影響最終的返回值
即try、catch中的return語句兩步拆開走,先把把返回值放到“操作數棧”中,然后走finally中的語句,最后結束當前方法,把這個“操作數棧”中的值返回給調用處
2 方式2:throws聲明拋出異常
2.1 throw關鍵字
在編寫程序時,我們必須要考慮程序出現問題的情況。比如,在定義方法時,方法需要接受參數。那么,當調用方法使用接受到的參數時,首先需要先對參數進行合法的判斷,數據若不合法,就應該告訴調用者,傳遞合法的數據進來。這時需要使用拋出異常的方式來告訴調用者。
異常的對象的創建和拋出有兩種方式:
(1)JVM創建並拋出
(2)程序員new出來,然后由throw拋出。
在java中,提供了一個throw關鍵字,它用來拋出一個指定的異常對象:
-
創建一個異常對象。封裝一些提示信息(信息可以自己編寫)
-
通過關鍵字throw,throw 異常對象,將這個異常對象告知給調用者
throw
用在方法內,用來拋出一個異常對象,將這個異常對象傳遞到調用者處,並結束當前方法的執行。
注意事項:
throw關鍵字必須寫在方法內部
throw關鍵字后邊創建的異常對象,必須是Exception或Exception的子類對象
throw關鍵字后邊創建的異常對象:
(1) 如果是運行期異常,那么我們不用處理這個異常,交給JVM處理,默認處理方式就是中斷處理
(2) 如果是編譯期異常,那么我們就必須處理這個異常,要么throws繼續拋出交給上層調用者處理,要么try...catch自己處理
使用格式:
throw new 異常類名([參數]);
例如:
throw new Exception();
throw new ClassNotFoundException("類型不存在!");
代碼示例:
public class ThrowsDemo {
public static void main(String[] args) throws ClassNotFoundException, FileNotFoundException {
method01(0);
}
private static void method01(int a) throws ClassNotFoundException, FileNotFoundException {
if (a == 0){
throw new ClassNotFoundException("類型不存在!");
}else {
throw new FileNotFoundException("文件不存在!");
}
}
}
那么對於調用者來說,該怎么處理呢?
對於運行期異常可以不做處理;對於編譯期異常:一種是使用 try...catch 進行捕獲處理,另一種就是使用
throws
關鍵字繼續將異常聲明出去,由方法調用者處理
2.3 throws關鍵字
聲明異常:將問題標識出來,報告給調用者。如果方法內拋出了編譯期異常,而沒有捕獲處理,那么必須通過throws
關鍵字進行聲明,讓調用者去處理。
關鍵字throws
用在方法聲明上,用於表示當前方法不處理異常,而是提醒該方法的調用者來處理異常
聲明異常格式:
修飾符 返回值類型 方法名(參數) throws xxxException, yyyException...{
...
}
聲明異常的代碼示例:
public class ThrowsDemo {
public static void main(String[] args) throws NullPointerException, ArrayIndexOutOfBoundsException {
int[] arr = null;
method01(arr);
}
private static void method01(int[] arr) throws NullPointerException, ArrayIndexOutOfBoundsException {
//Objects.requireNonNull(arr);
if (arr.length==0){
throw new ArrayIndexOutOfBoundsException("越界了");
}
if (arr == null){
throw new NullPointerException();
}
}
}
注意事項:
- throws關鍵字必須寫在方法聲明處
- throws關鍵字后邊聲明的異常類名必須是Exception或Exception的子類
一般方法內部拋出什么異常類型,就聲明什么異常對象 - 如果方法內部拋出了多個異常對象,那么在throws后面可以寫多個異常類,用逗號隔開。
如果拋出的多個異常對象有子父類關系,那么我們聲明父類異常即可 - 如果調用了一個聲明拋出編譯期異常的方法,那么我們就必須處理這個異常對象:
a.可以使用throws繼續聲明拋出這個異常,最終交由JVM處理 --> 中斷處理
b.可以使用try...catch自己處理這個異常 - 運行期異常可以不處理,即不捕獲也不聲明拋出,交給JVM處理(中斷))
- 如果父類拋出了多個異常,子類重寫父類方法時,拋出和父類相同的異常或者是父類異常的子類或者不拋出異常。
- 父類方法沒有拋出異常,子類重寫父類該方法時也不可拋出異常。如果子類產生異常,只能捕獲處理,不能聲明拋出