1、什麼是異常?
異常,就是指程序在運行時出現不正常的情況。
異常也是某種意義上的錯誤,就是問題,雖然編譯通過了,但會導致運行失敗。
(1)異常的由來:
問題也是現實中的一個具體的事物,也可以通過Java類的形式進行描述,並封裝成對象。
其實就是Java對不正常情況進行描述後的對象體現。
問題封裝成對象。
(2)對於問題的劃分,分爲兩種:
一種是嚴重的問題,一種是非嚴重的問題。
對於嚴重的,Java通過Error類進行描述。
對於Error,一般不編寫針對性的代碼對其進行處理。
對於不嚴重的,Java通過Exception類進行描述。
對於Exception,可以使用針對性的處理方式進行處理。
無論Error還是Exception,都具有一些共性的內容。
比如:不正常的信息,引發原因等。
函數有異常發生時,函數就停止,所以兩個異常不能同時處理。
2、異常的處理
Java提供了特有的語句對異常進行處理。
try
{
需要被檢測的代碼;
}
catch(異常類 變量)
{
處理異常的代碼;
}
finally
{
一定會執行的語句;
}
代碼示例:
class ExceptionDemo {
public static void main(String[] args){
Demo d = new Demo();
try{
int f = d.div(5,0); //函數有異常發生,函數就停止,所以兩個異常不能同時處理。
System.out.println(f);
}
catch(Exception e){
System.out.println(e.toString()); //捕獲異常,並處理
System.out.println("除零啦");
}
System.out.println("over");
}
}
class Demo {
int div(int a,int b) throws Exception {
return a/b; //會出現異常,除數不爲零
}
}
出現異常不進行處理時,默認打印異常信息:
對異常用try-catch進行處理後:
(1)對捕獲到的異常進行常見方法操作:
//e爲異常對象
e.toString(); //打印異常的簡短描述
String getMessage(); //獲取異常的信息
e.printStackTrace(); //JVM默認的異常處理機制,就是在調用printStackTrace()方法打印異常的堆棧跟蹤信息。
(2)在函數上聲明異常,即 throws Exception
便於提高安全性,要捕獲進行處理,不處理編譯失敗。
函數後使用 throws 關鍵字聲明此函數可能會出現問題。
3、對多異常的處理
(1)聲明異常時,建議聲明爲更具體的異常,這樣處理的可以更具體。
例如,上面除數爲0的異常爲算數異常,可以聲明爲ArithmeticException,更爲具體。
(2)對方聲明幾個異常,就對應幾個catch塊,不要定義多餘的catch塊。
如果多個catch塊中的異常出現繼承關係,父類異常catch塊放在最下面.(ArithmeticException就繼承了Exception)
否則,只執行父類異常的catch塊。
(3)異常中的多態性,父類異常也是多態性的體現,拋出的異常對象可以是父類引用。
(4)建議在進行catch處理時,catch中一定要定義具體處理方式,不要簡單定義一句輸出語句 e.printStackTrace(),而是輸出爲一個異常日誌文件。
多異常處理的Demo:
class ExceptionDemo {
public static void main(String[] args){
Demo d = new Demo();
try{
int f = d.div(5,0); //函數有異常發生,函數就停止,所以兩個異常不能同時處理。
System.out.println(f);
}
catch(ArithmeticException e){
System.out.println(e.toString());//捕獲算數異常,並處理
System.out.println("除零啦");
}
catch(ArrayIndexOutOfBoundsException c){ //捕獲腳標越界異常,並處理
System.out.println(c.toString());
System.out.println("數組腳標越界啦");
}
catch(Exception e) { //父類異常放在最下面,否則只執行父類異常
System.out.println("haha:"+e.toString());
}
System.out.println("over");
}
}
class Demo {
int div(int a,int b) throws ArithmeticException,ArrayIndexOutOfBoundsException {
int[] arr = new int[a];
System.out.println(arr[4]); //會出現腳標越界異常
return a/b; //會出現算數異常,除數不爲零
}
}
結果爲除數爲0異常:
把
int f = d.div(5,0);
改爲
int f = d.div(4,1);
就會出現數組腳標越界異常:
3、自定義異常
因爲項目中會出現特有的問題,而這些問題並未被Java所描述並封裝對象。
所以對於這些特有的問題,可以按照Java的對問題封裝的思想,將特有的問題,進行自定義的異常封裝。
那麼就需要對這個問題進行自定義的描述。
(1)當在函數內部出現了 throw 拋出異常對象,那麼就必須給出對應的異常處理:
要麼在內部 try-catch 處理;
要麼在函數上聲明異常,讓調用者處理,
一般情況下,函數內出現異常,函數上需要聲明。
(2)發現打印的結果中只有異常的名稱,卻沒有異常的信息,因爲自定義的異常並未定義信息。
(3)如何定義異常信息呢?
因爲父類中已經把異常信息的操作都完成了,
所以子類只要在構造時,通過 super() 語句,將異常信息傳遞給父類,
那麼就可以直接通過 getMessage() 方法獲取自定義的異常。
(4)自定義異常:
自定義異常類必須繼承 Exception。
繼承Exception 的原因:
異常體系有個特點:因爲異常類和異常對象都被拋出;
他們都具備可拋性,這個可拋型是 Throwable 這個體系中獨有的特點;
Throwable 是 Exception 和 Error 的父類,
只有這個體系中的類和對象纔可以被 throws 和 throw 操作。
(5)throws 和 throw 的區別
a) throws 使用在函數上(大括號和小括號之間)。throw 使用在函數內。
b) throws 後面跟的是異常類,可以跟多個(多異常),用逗號隔開。
throw 後面跟的是異常對象(new關鍵字)。
c) 一般情況下,函數內拋出異常,則該函數上必須拋出異常類。
RuntimeException 是特例。
自定義異常的Demo:
// 需求:在本程序中,對於除數是-1,也視爲是錯誤的是無法進行運算的,即自定義的異常。
class ZiDingYiException {
public static void main(String[] args){
Demo d = new Demo();
try {
int f = d.div(5,-1); //函數有異常發生,函數就停止,所以兩個異常不能同時處理。
System.out.println(f);
}
catch(ArithmeticException e){
System.out.println(e.getMessage());
System.out.println("除零啦");
}
catch(FuShuException e){
System.out.println(e.getMessage());
System.out.println("除數出現負數了");
}
System.out.println("over");
}
}
class Demo {
int div(int a,int b) throws ArithmeticException,FuShuException { //會出現除數爲0異常和負數異常
if(b<0){
throw new FuShuException("出現了除數是負數的情況:/by fushu"); //手動通過throw關鍵字拋出異常對象,括號內爲定義的異常信息
}
return a/b;
}
}
class FuShuException extends Exception{ //自定義的異常類中,需通過構造函數傳遞異常信息
private String msg;
FuShuException(String msg){ //利用構造函數傳遞異常信息
this.msg = msg;
}
public String getMessage(){ //重寫getMessage;給異常定義信息
return msg;
}
}
/* 因爲父類中已經把異常信息的操作都完成了。
所以子類只要在構造時,將異常信息傳遞給父類通過super()語句。
那麼就可以直接通過getMessage()方法獲取自定義的異常。 */
/*
即 FuShuException() 函數中的三個語句,可以替換爲:
FuShuException(String msg){
super(msg);
}
即 FuShuException類如下:
*/
class FuShuException extends Exception{
FuShuException(String msg){ //利用構造函數傳遞異常信息
super(msg); //通過 super()語句,將異常信息傳遞給父類
}
}
運行結果:
4、RuntimeException
(1)Exception中有一個特殊的子類異常,即 RuntimeException;
如果在函數內拋出該異常,函數上可以不用聲明,編譯一樣通過;
如果在函數上聲明瞭該異常,調用者可以不用進行處理,編譯一樣通過;
之所以不用在函數上聲明,是因爲不需要讓調用者處理。當該異常發生,希望程序停止。
因爲在運行時,出現了無法繼續運算的情況,希望停止程序後,對代碼進行修正。
(2)自定義異常時:
如果該異常發生,無法再繼續進行運算時,就讓自定義的異常繼承 RuntimeException 。
(3)對於異常分爲兩種:
a) 編譯時被檢測的異常,Exception;
b) 編譯時不被檢測的異常(運行時異常,RuntimeException及其子類)
運行時異常,函數內拋出異常對象,函數上不用聲明異常,也不用在調用時處理異常。
RuntimeException的Demo:
class RuntimeException {
public static void main(String[] args){
Demo d = new Demo();
int f = d.div(5,0);
System.out.println(f);
System.out.println("over");
}
}
class Demo {
int div(int a,int b){ //throws ArithmeticException,因爲ArithmeticException是RuntimeException的子類
if(b==0){
throw new ArithmeticException(); //手動通過throw關鍵字拋出異常對象。
}
return a/b; //會出現算數異常,除數不爲零
}
}