異常:異常是指中斷程序正常執行的一種指令流
在JAVA程序中一旦出現了異常,而程序又沒有及時處理的情況下,那麼程序會中斷執行。
例如,觀察以下的代碼:
public class Demo22{
public static void main(String args[]){
int i = 10 ;
int j = 0 ;
System.out.println(" --------- 計算開始 ---------") ;
System.out.println("i / j = " + (i / j)) ; à 此段代碼出現了異常
System.out.println(" --------- 計算結束 ---------") ;
}
}; |
以上代碼編譯的時候不存在任何的問題,那麼執行時,出錯了,打印內容如下:
--------- 計算開始 ---------
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Demo22.main(Demo22.java:6) |
整個程序,在出現異常的代碼之後不再被執行了,異常之前的程序呢?正確的執行了。
那麼在JAVA中我們所處理的異常都屬於運行時異常,即:執行JAVA程序的時候出現的錯誤,那麼實際上對於JAVA的異常分類應該按如下的形式劃分:
ArithmeticException à RuntimeException à Exception(往往是要由用戶自己處理的)
所以對於異常而言,它是一個統稱,代表了:
Throwable(可能的拋出)
|- Exception:表示需要人爲處理的異常,指的是程序的錯誤 à 因爲程序的錯誤較多,所以就習慣了把所有的錯誤都統一成爲異常。
|- Error:表示JVM錯誤,人爲不能處理
異常的處理格式如下:
try{
包含可能出現錯誤的代碼段 ;
}catch(出錯的類型 出錯的對象){
對於錯誤的處理語句 ;
} |
那麼使用如上的格式修改之前的代碼,保證程序可以正確的執行完:
public class Demo23{
public static void main(String args[]){
int i = 10 ;
int j = 0 ;
System.out.println(" --------- 計算開始 ---------") ;
try{
System.out.println("i / j = " + (i / j)) ;
}catch(ArithmeticException e){
System.out.println("計算出現了錯誤。。。") ;
}
System.out.println(" --------- 計算結束 ---------") ;
}
}; |
但是以上的程序依然存在了些小小的問題,例如:對於計算,往往應該由用戶自己輸入數據,所以此時可以通過接收參數爲其輸入數據。代碼如下:
public class Demo24{
public static void main(String args[]){
int i = 10 ;
int j = 0 ;
System.out.println(" --------- 計算開始 ---------") ;
try{
i = Integer.parseInt(args[0]) ;
j = Integer.parseInt(args[1]) ;
System.out.println("i / j = " + (i / j)) ;
}catch(ArithmeticException e){
System.out.println("計算出現了錯誤。。。") ;
}
System.out.println(" --------- 計算結束 ---------") ;
}
}; |
如果現在輸入的內容是正確的數字,則程序不會有任何的問題,但是既然數據是由用戶自己輸入的,那麼肯定會輸入各種違法的數據。
存在的問題:
· 不輸入任何參數,出現數組越界:ArrayIndexOutOfBoundsException
· 輸入的參數不是數字,出現了數字格式轉換錯誤:NumberFormatException
· 輸入的被除數是0,出現算術錯誤:ArithmeticException
在以上的錯誤之中,只有算術錯誤被處理了,因爲在程序上具備了算術錯誤的處理能力。那麼也就意味着,現在的異常處理並不完善,需要加入更多的處理,所以需要編寫更多的catch。
以上的程序修改爲:
public class Demo24{
public static void main(String args[]){
int i = 10 ;
int j = 0 ;
System.out.println(" --------- 計算開始 ---------") ;
try{
i = Integer.parseInt(args[0]) ;
j = Integer.parseInt(args[1]) ;
System.out.println("i / j = " + (i / j)) ;
}catch(ArithmeticException e){
System.out.println("算術錯誤。。。"+e) ;
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("沒有輸入參數。。。"+e) ;
}
catch(NumberFormatException e){
System.out.println("輸入的內容不是數字。。。"+e) ;
}
System.out.println(" --------- 計算結束 ---------") ;
}
}; |
但是會發現比較麻煩,因爲現在已經知道了的是三個錯誤,那麼對於很多未知道的錯誤呢?那麼此時,就需要分析異常的產生過程。
發現catch中接收的內容都是對象,那麼也就意味着,所謂的異常,就是自動產生了一個異常類的實例化對象。一出錯就產生。
對象有一個特點,可以向上自動轉型,Exception是用戶需要處理的最大異常,那麼證明所有的對象都可以向Exception轉換。
public class Demo25{
public static void main(String args[]){
int i = 10 ;
int j = 0 ;
System.out.println(" --------- 計算開始 ---------") ;
try{
i = Integer.parseInt(args[0]) ;
j = Integer.parseInt(args[1]) ;
System.out.println("i / j = " + (i / j)) ;
}catch(ArithmeticException e){
System.out.println("算術錯誤。。。"+e) ;
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("沒有輸入參數。。。"+e) ;
}
catch(NumberFormatException e){
System.out.println("輸入的內容不是數字。。。"+e) ;
}
catch(Exception e){
System.out.println("其他異常。。。"+e) ;
}
System.out.println(" --------- 計算結束 ---------") ;
}
}; |
注意:
如果在程序中出現了多個catch語句,則捕獲更粗的catch要放在捕獲更細的catch之後。
public class Demo25{
public static void main(String args[]){
int i = 10 ;
int j = 0 ;
System.out.println(" --------- 計算開始 ---------") ;
try{
i = Integer.parseInt(args[0]) ;
j = Integer.parseInt(args[1]) ;
System.out.println("i / j = " + (i / j)) ;
}catch(Exception e){
System.out.println("其他異常。。。"+e) ;
}catch(ArithmeticException e){
System.out.println("算術錯誤。。。"+e) ;
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("沒有輸入參數。。。"+e) ;
}catch(NumberFormatException e){
System.out.println("輸入的內容不是數字。。。"+e) ;
}
System.out.println(" --------- 計算結束 ---------") ;
}
}; |
則編譯的時候出現了以下的錯誤,說是許多的異常已經被捕獲了。
Demo25.java:14: 已捕捉到異常 java.lang.ArithmeticException
catch(ArithmeticException e){
^
Demo25.java:17: 已捕捉到異常 java.lang.ArrayIndexOutOfBoundsException
catch(ArrayIndexOutOfBoundsException e){
^
Demo25.java:20: 已捕捉到異常 java.lang.NumberFormatException
catch(NumberFormatException e){
^
3 錯誤 |
既然所有異常類的對象都可以向Exception轉換,那麼是不是直接使用Exception接收更方便?
public class Demo26{
public static void main(String args[]){
int i = 10 ;
int j = 0 ;
System.out.println(" --------- 計算開始 ---------") ;
try{
i = Integer.parseInt(args[0]) ;
j = Integer.parseInt(args[1]) ;
System.out.println("i / j = " + (i / j)) ;
System.out.println("***************************") ;
}catch(Exception e){
System.out.println(e) ;
}
System.out.println(" --------- 計算結束 ---------") ;
}
}; |
以上是異常處理的一種格式,實際上異常處理還有另外一種格式,就是說,可以爲異常做一個統一的出口,不管是否發生了異常,都要執行此代碼:
try{
包含可能出現錯誤的代碼段 ;
}catch(出錯的類型 出錯的對象){
對於錯誤的處理語句 ;
}finally{
不管是否出現異常,都執行此代碼 ;
} |
代碼如下:
public class Demo27{
public static void main(String args[]){
int i = 10 ;
int j = 0 ;
System.out.println(" --------- 計算開始 ---------") ;
try{
i = Integer.parseInt(args[0]) ;
j = Integer.parseInt(args[1]) ;
System.out.println("i / j = " + (i / j)) ;
}catch(Exception e){
System.out.println(e) ;
}finally{ à 典型的程序出口
System.out.println("不管是否存在異常,此代碼都會被執行。。。") ;
}
System.out.println(" --------- 計算結束 ---------") ;
}
}; |
在異常處理中還有兩個很重要的關鍵字
· throws:在方法的聲明處使用,表示此方法不處理異常,而交給被調用處處理。
· throw:表示人爲的拋出一個異常。
例如:定義一個數學類,裏面有一個除法
class Math{
// 此代碼有可能發生問題,也有可能不發生問題
// 一旦發生問題,找調用處,誰調用的此方法,誰去處理
public int div(int i,int j) throws Exception{
return i / j ;
}
};
public class Demo28{
public static void main(String args[]){
Math m = new Math() ;
System.out.println(m.div(10,2)) ;
}
}; |
按規定來說,此方法在調用的時候使用異常的捕獲,如果不使用,則編譯時出現以下的錯誤提示:
Demo28.java:11: 未報告的異常 java.lang.Exception;必須對其進行捕捉或聲明以便拋出
System.out.println(m.div(10,2)) ;
^
1 錯誤 |
所以對於調用有throws聲明的方法,必須使用try…catch:
class Math{
// 此代碼有可能發生問題,也有可能不發生問題
// 一旦發生問題,找調用處,誰調用的此方法,誰去處理
public int div(int i,int j) throws Exception{
return i / j ;
}
};
public class Demo28{
public static void main(String args[]){
Math m = new Math() ;
try{
System.out.println(m.div(10,0)) ;
}catch(Exception e){
System.out.println(e) ;
}
}
}; |
問?既然main方法是一個方法,那麼能不能在主方法上編寫throws語句呢?肯定是可以的
class Math{
// 此代碼有可能發生問題,也有可能不發生問題
// 一旦發生問題,找調用處,誰調用的此方法,誰去處理
public int div(int i,int j) throws Exception{
return i / j ;
}
};
public class Demo29{
public static void main(String args[]) throws Exception{
Math m = new Math() ;
System.out.println(m.div(10,0)) ;
}
}; |
觀察錯誤的提示:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Math.div(Demo29.java:5)
at Demo29.main(Demo29.java:11) |
可以發現以上的錯誤提示與不使用try…catch效果是一樣的,證明所有的錯誤都交給了JVM進行處理,實際上在JAVA中默認的處理方式也就是使用JVM完成。
throw:表示在程序中人爲的拋出一個異常,而且必須使用try…catch進行處理。
public class Demo30{
public static void main(String args[]){
try{
throw new Exception("我自己樂意拋。。。") ;
}catch(Exception e){
System.out.println(e) ;
}
}
}; |
因爲對於異常而言,最終結果肯定都是拋出一個異常的實例化對象。所以是自己拋還是系統拋都肯定需要進行異常的捕獲及處理。
設計一個除法操作,在操作之前編寫:計算開始,在操作之後編寫計算結束,中間不管是否出現了錯誤,都要打印這樣兩句話,問:該如何實現?
class Math{
public int div(int i,int j) throws Exception{
System.out.println(" ======== 計算開始 =========") ;
int temp = 0 ;
try{
temp = i / j ;
}catch(Exception e){
throw e ;
}finally{
System.out.println(" ======== 計算結束 =========") ;
}
return temp ;
}
};
public class Demo31{
public static void main(String artgs[]){
Math m = new Math() ;
try{
System.out.println(m.div(1,0)) ;
}catch(Exception e){
System.out.println(e) ;
}
}
}; |
一般而言,對於throw、throws、finally往往會一起使用。
自定義異常:
一個類只要繼承了Exception就可以稱爲一個異常類。
class MyException extends Exception{
public MyException(String msg){
super(msg) ;
}
};
public class Demo32{
public static void main(String args[]){
try{
throw new MyException("自己定義玩的。。") ;
}catch(Exception e){
System.out.println(e) ;
}
}
}; |
注意點:
· throw與throws的區別?
|- throw:是人爲拋出異常
|- throws:方法聲明處使用,表示在方法中不處理異常
· final與finally
|- final:定義類、方法、常量
|- finally:異常的統一出口