合理使用異常處理,可以讓程序更加健壯。
異常的產生
異常是導致程序中斷執行的一種指令流。當異常出現時,如果沒有合理處理,程序就會中斷執行。
範例:不產生異常的代碼
public class Demo {
public static void main(String[] args) {
System.out.println("1.除法計算開始");
System.out.println("2.出發計算:10 / 2 = " + (10 / 2));
System.out.println("3.除法計算結束");
}
}
範例:產生異常
public class Demo {
public static void main(String[] args) {
System.out.println("1.除法計算開始");
// 2.中將出現異常
System.out.println("2.出發計算:10 / 2 = " + (10 / 0));
System.out.println("3.除法計算結束");
}
}
// 結果爲:
// 1.除法計算開始
// Exception in thread "main" java.lang.ArithmeticException: / by zero
// at com.java.util.Demo.main(Demo.java:7)
異常產生後,產生異常的語句以及之後的語句將不再執行。默認情況下系統會輸出異常信息,而後自動結束程序的執行。
異常的處理
1、Java中進行處理異常,使用try
、catch
、finally
這三個關鍵字,語法如下:
try {
// 可能出現異常的語句
} catch (異常類型 對象1) {
// 異常處理
} catch (異常類型 對象2) {
// 異常處理
} finally {
// 不論是否出現異常都執行的語句
}
對於上述操作的組合有:try…catch
、try…catch…finally
、try…finally
(這個不建議使用)。
範例:應用異常處理格式
public class Demo {
public static void main(String[] args) {
System.out.println("1.除法計算開始");
try {
System.out.println("2.出發計算:10 / 2 = " + (10 / 0));
} catch (ArithmeticException e) {
}
System.out.println("3.除法計算結束");
}
}
// 結果爲:
// 1.除法計算開始
// 3.除法計算結束
2、出現異常就要處理異常,爲了能進行異常處理,可以使用異常類中的printStackTrace()
輸出完整的異常信息:
public class Demo {
public static void main(String[] args) {
System.out.println("1.除法計算開始");
try {
System.out.println("2.出發計算:10 / 2 = " + (10 / 0));
} catch (ArithmeticException e) {
e.printStackTrace();
}
System.out.println("3.除法計算結束");
}
}
// 結果爲:
// 1.除法計算開始
// java.lang.ArithmeticException: / by zero
// at com.java.util.Demo.main(Demo.java:7)
// 3.除法計算結束
範例:使用try…catch…finally
public class Demo {
public static void main(String[] args) {
System.out.println("1.除法計算開始");
try {
System.out.println("2.出發計算:10 / 2 = " + (10 / 0));
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
System.out.println("不論是否異常,都執行");
}
System.out.println("3.除法計算結束");
}
}
3、異常捕獲時,一個try語句可以跟多個catch語句。
public class Demo {
public static void main(String[] args) {
System.out.println("1.除法計算開始");
try {
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
System.out.println("2.出發計算:" + (x / y));
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
System.out.println("不論是否異常,都執行");
}
System.out.println("3.除法計算結束");
}
}
上述程序將由用戶輸入數據,可能存在以下異常:
· 用戶執行時不輸入參數(java Demo):java.lang.ArrayIndexOutOfBoundsException
數組越界錯誤;
· 輸入的數據不是數字(java Demo a b):java.lang.NumberFormatException
;
· 被除數爲0(java Demo 10 0):java.lang.ArithmeticException
以上代碼只有一個catch,只能處理一個異常,其他異常依然會導致程序中斷
範例:增加多個catch
public class Demo {
public static void main(String[] args) {
System.out.println("1.除法計算開始");
try {
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
System.out.println("2.出發計算:" + (x / y));
} catch (ArithmeticException e) {
e.printStackTrace();
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
} finally {
System.out.println("不論是否異常,都執行");
}
System.out.println("3.除法計算結束");
}
}
異常處理流程
1、觀察兩個異常類的繼承結構:
NumberFormatException:
java.lang.Object
|- java.lang.Throwable
|- Exception
|- RuntimeException
|- IllegalArgumentException
|- NumberFormatException
ArithmeticException:
java.lang.Object
|-java.lang.Throwable
|- Exception
|- RuntimeException
|- ArithmeticException
由上表可得,所有異常類都是Throwable
的子類。Throwable下有兩個子類Error
和Exception
。
請解釋Error和Exception的區別:
· Error:指的是JVM錯誤,即:此時程序還沒有執行,用戶不能處理;
· Exception:指的是程序運行中產生的異常,用戶可以處理。
所謂的異常處理指的是Exception以及它的子類異常。
2、異常處理流程圖
流程描述:
1)當程序運行時出現異常,由JVM自動根據異常類型實例化一個與之類型匹配的異常類對象;
2)產生異常對象後,會判斷當前語句是否存在異常處理。如果沒有異常處理,就交給JVM進行默認的異常處理(輸出異常信息,結束程序調用);
3)如果有異常捕獲操作,會由try語句捕獲產生的異常類實例化對象,之後與catch語句進行比較,如果有符合的捕獲類型,則使用catch語句進行異常處理;如果不匹配,則繼續向下匹配其它catch語句;
4)不論異常處理是否能夠匹配,都要繼續執行,如果程序中存在finally語句,就先執行finally語句中的代碼,執行完畢後根據之前catch匹配結果來決定如何執行,如果之前成功捕獲異常,那就繼續執行finally之後的語句;如果沒有成功捕獲,就交給JVM進行默認處理。
整個過程和catch中的異常類型進行匹配,但是所有Java對象都可以自動向上轉型。即如果真的要匹配類型,簡單的做法就是匹配Exception。
public class Demo {
public static void main(String[] args) {
System.out.println("1.除法計算開始");
try {
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
System.out.println("2.出發計算:" + (x / y));
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("不論是否異常,都執行");
}
System.out.println("3.除法計算結束");
}
}
上述將所有的異常都交由Exception類處理,因此程序無法知道具體產生的是什麼異常。
說明:
· 使用多個catch時,範圍大的異常一定要放在範圍小的異常後面,否則會出現語法錯誤。
· 直接捕獲Exception比較方便,但不合理,因爲所有異常都按照同種方式處理。項目中應根據具體異常類型處理。
throws關鍵字
1、throws關鍵字主要用於方法聲明,將異常交由被調用處(如main方法)處理。
範例:使用throws關鍵字
class MyMath {
public static int div(int x, int y) throws Exception {
// 使用了throws,所以該方法產生的異常交由調用處處理
return x / y;
}
}
範例:調用上述方法
public class Demo {
public static void main(String[] args) {
// 必須進行異常處理,否則代碼報錯
try {
System.out.println(MyMath.div(10, 2));
} catch (Exception e) {
e.printStackTrace();
}
}
}
調用了具有throws聲明的方法,不論操作是否異常,都需要使用try..catch
進行異常處理。
2.在主方法使用throws關鍵字後,異常將交給JVM處理,即採用默認處理方式。由於開發的程序多數希望正常結束調用,因此主方法不應該使用throws關鍵字。
public class Demo {
public static void main(String[] args) throws Exception {
System.out.println(MyMath.div(10, 0));
}
}
throw關鍵字
1、程序中可以使用throw手工拋出一個異常類的對象。
public class Demo {
public static void main(String[] args) {
try {
throw new Exception("自定義異常");
} catch (Exception e) {
e.printStackTrace();
}
}
}
throws與throw的區別:
·throw:在方法中手工拋出一個異常類對象(該對象可以是自定義的,或者已經存在的);
·throws:用於方法聲明上,使得調用該方法時必須處理異常。
異常處理標準格式
要求:定義div(),在執行除法前打印提示信息,在計算結束後打印提示信息;如果計算中產生了異常,交給調用處處理。
範例:
class MyMath {
public static void div(int x, int y) {
System.out.println("==== 除法計算開始 ====");
System.out.println(x / y);
System.out.println("==== 除法計算結束 ====");
}
}
public class Demo {
public static void main(String[] args) {
MyMath.div(10,2);
}
}
上述代碼可能出現異常,因此要進行異常處理。根據要求,異常交由調用處處理,因此使用throws關鍵字。
class MyMath {
// 如果div()出現異常,異常交給調用處處理
public static void div(int x, int y) throws Exception {
System.out.println("==== 除法計算開始 ====");
System.out.println(x / y);
System.out.println("==== 除法計算結束 ====");
}
}
public class Demo {
public static void main(String[] args) {
try {
MyMath.div(10, 2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
上述代碼產生錯誤後,程序運行到System.out.println("==== 除法計算結束 ====");
就不執行了。
範例:正確做法如下:
class MyMath {
// 如果div()出現異常,異常交給調用處處理
public static void div(int x, int y) throws Exception {
System.out.println("==== 除法計算開始 ====");
try {
System.out.println(x / y);
} catch (Exception e) {
throw e; // 拋出異常
} finally {
System.out.println("==== 除法計算結束 ====");
}
}
}
public class Demo {
public static void main(String[] args) {
try {
MyMath.div(10, 0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
RuntimeException類
範例:觀察下述程序
public class Demo {
public static void main(String[] args) {
int temp = Integer.parseInt("100");
}
}
parseInt(): public static int parseInt(String s) throws NumberFormatException;
parseInt()拋出了NumberFormatException,按照之前知識點,此處應強制進行異常捕獲,但實際並沒有該要求:
觀察一下NumberFormatException的繼承結構:
java.lang.Object
|- java.lang.Throwable
|- java.lang.Exception
|- java.lang.RuntimeException → 運行時異常
|- java.lang.IllegalArgumentException
|- java.lang.NumberFormatException
Java爲方便代碼編寫,提供了RuntimeException類,該類的特徵是:程序在編譯時,不會強制性要求用戶處理異常,用戶可以根據自己的需求選擇性處理,但是沒有處理又發生異常,就會交給JVM默認處理。
請解釋Exception與RuntimeException的區別,請列舉常見的幾種RuntimeException
· Exception是RuntimeExceptio的父類;
· 使用Exception定義的異常必須要被處理,而RuntimeException的異常可以選擇性處理。
·常見的RuntimeException:ArithmeticException、NullPointerException、ClassCastException。
異常的捕獲及處理(斷言)
assert關鍵字在JDK1.4引入,其功能是進行斷言。
public class Demo {
public static void main(String[] args) {
int num = 10;
assert num == 20 : "num 不等於20";
System.out.println("num = " + num);
}
}
默認情況下,斷言是不應該影響程序的運行,即Java在解釋程序時,斷言是默認不起作用的。
啓用斷言:java -ea Demo
Exception in thread "main" java.lang.AssertionError: num的內容不是20
at Demo.main(Demo.java:6)
異常的捕獲及處理(自定義異常)
- Java自身提供了大量的異常類,但對於實際開發是不夠。例如:進行添加數據操作時,可能出現錯誤數據,錯誤數據出現就應該拋出異常,例如AddException,而該異常Java沒有,需要自己開發。
如果要自己開發一個異常類可以選擇繼承Exception或RuntimeException。
範例:定義AddException
class AddException extends Exception {
public AddException(String msg) {
super(msg);
}
}
public class Demo {
public static void main(String[] args) {
int num = 11;
try {
if (num > 10) {
throw new AddException("數值傳遞過大");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
上述代碼,只是介紹自定義異常的形式,不能說明自定義異常的作用。