閱讀471 返回首頁    go 阿裏雲 go 技術社區[雲棲]


JAVA【異常二】異常處理機製

Java中異常提供了一種識別及響應錯誤情況的一致性機製,有效地異常處理能使程序更加健壯、易於調試。異常之所以是一種強大的調試手段,在於其回答了以下三個問題:

  • 什麼出了錯?
  • 在哪出的錯?
  • 為什麼出錯?

在有效使用異常的情況下,異常類型回答了“什麼”被拋出,異常堆棧跟蹤回答了“在哪“拋出,異常信息回答了“為什麼“會拋出。

 

在Java 應用程序中,異常處理機製為:拋出異常,捕捉異常。

拋出異常:當一個方法出現錯誤引發異常時,方法創建異常對象並交付運行時係統,異常對象中包含了異常類型和異常出現時的程序狀態等異常信息。運行時係統負責尋找處置異常的代碼並執行。

捕獲異常:在方法拋出異常之後,運行時係統將轉為尋找合適的異常處理器(exception handler)。潛在的異常處理器是異常發生時依次存留在調用棧中的方法的集合。當異常處理器所能處理的異常類型與方法拋出的異常類型相符時,即為合適的異常處理器。運行時係統從發生異常的方法開始,依次回查調用棧中的方法,直至找到含有合適異常處理器的方法並執行。當運行時係統遍曆調用棧而未找到合適 的異常處理器,則運行時係統終止。同時,意味著Java程序的終止。

 

對於運行時異常、錯誤或可查異常,Java技術所要求的異常處理方式有所不同。

由於運行時異常的不可查性,為了更合理、更容易地實現應用程序,Java規定,運行時異常將由Java運行時係統自動拋出,允許應用程序忽略運行時異常。

 對於方法運行中可能出現的Error,當運行方法不欲捕捉時,Java允許該方法不做任何拋出聲明。因為,大多數Error異常屬於永遠不能被允許發生的狀況,也屬於合理的應用程序不該捕捉的異常。

 對於所有的可查異常,Java規定:一個方法必須捕捉,或者聲明拋出方法之外。也就是說,當一個方法選擇不捕捉可查異常時,它必須聲明將拋出異常。

 能夠捕捉異常的方法,需要提供相符類型的異常處理器。所捕捉的異常,可能是由於自身語句所引發並拋出的異常,也可能是由某個調用的方法或者Java運行時係統拋出的異常。也就是說,一個方法所能捕捉的異常,一定是Java代碼在某處所拋出的異常。簡單地說,異常總是先被拋出,後被捕捉的。

 Java異常的捕獲和處理是一個不容易把握的事情,如果處理不當,不但會讓程序代碼的可讀性大大降低, 而且導致係統性能低下,甚至引發一些難以發現的錯誤。 

 


 
Java異常處理涉及到五個關鍵字,分別是:try、catch、finally、throw、throws。下麵將驟一介紹, 通過認識這五個關鍵字,掌握基本異常處理知識。 

   

1、捕獲異常:try、catch和 finally

 Java中,異常通過try-catch語句捕獲。其一般語法形式為:

  1. 1.  try {    
  2. 2.      // 被檢測的代碼  
  3. 3.       //可能出現的代碼  
  4. 4.  } catch (異常類名 變量){    
  5. 5.      // 異常的 處理方式   
  6. 6.  }   

  關鍵詞try後的一對大括號將一塊可能發生異常的代碼包起來,稱為監控區域。Java方法在運行過程中出現異常,則創建異常對象。將異常拋出監控區域之外,由Java運行時係統試圖尋找匹配的catch子句以捕獲異常。若有匹配的catch子句,則運行其異常處理代碼,try-catch語句結束。

 匹配的原則是:如果拋出的異常對象屬於catch子句的異常類,或者屬於該異常類的子類,則認為生成的異常對象與catch塊捕獲的異常類型相匹配。

捕捉運行時係統自動拋出“除數為0”引發的ArithmeticException異常:

  1. public class ExceptionDemo1 {  
  2.     public static void main(String[] args) {    
  3.         int a = 10;    
  4.         int b = 0;    
  5.         try {       
  6.           int c=a/b;  
  7.         }    
  8.         catch (ArithmeticException e) {   
  9.            System.out.println("算術異常,除數為0");  
  10.         }    
  11.     }    
  12. }  

運行結果:算術異常,除數為0


2、      try-catch-finally

 try-catch語句還可以包括第三部分,就是finally子句。它表示無論是否出現異常,都應當執行的內容。try-catch-finally語句的一般語法形式為:

  1. 1.  try {    
  2. 2.      // 被檢測的代碼  
  3. 3.       //可能出現的代碼  
  4. 4.  } catch (異常類名 變量){    
  5. 5.      // 異常的 處理方式   
  6. 6.  }  finally{  
  7. 7.  必須要執行的代碼  
  8. 8.  } 

try 塊:用於捕獲異常。其後可接零個或多個catch塊,如果沒有catch塊,則必須跟一個finally塊。
catch 塊:用於處理try捕獲到的異常。
finally 塊:無論是否捕獲或處理異常,finally塊裏的語句都會被執行。當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回之前被執行。在以下4種特殊情況下,finally塊不會被執行:
1
)在finally語句塊中發生了異常。
2)在前麵的代碼中用了System.exit()退出程序。
3)程序所在的線程死亡。
4)關閉CPU。

捕捉數組下標越界異常:

  1. 9.  public class ExceptionDemo2 {  
  2. 10.   
  3. 11.     public static void main(String args[]) {    
  4. 12.              
  5. 13.             int[] a = new int[3];  
  6. 14.               
  7. 15.                 try {    
  8. 16.                     a[3] = 10;  
  9. 17.                 } catch (ArrayIndexOutOfBoundsException e) {    
  10. 18.                     System.out.println("數組下標越界異常");    
  11. 19.                 } finally {    
  12. 20.                     System.out.println("最終始終要執行");    
  13. 21.                 }  
  14. 22.          }  
  15. 23. }  

運行結果:數組下標越界異常

                最終始終要執行


3. try-catch-finally 規則(異常處理語句的語法規則):

1)  必須在try之後添加 catch 或 finally 塊。try 塊後可同時接 catch 和finally 塊,但至少有一個塊。
2) 必須遵循塊順序:若代碼同時使用 catch 和finally 塊,則必須將 catch 塊放在 try 塊之後。
3) catch 塊與相應的異常類的類型相關。
4) 一個 try 塊可能有多個 catch 塊。若如此,則執行第一個匹配塊。即Java虛擬機會把實際拋出的異常對象依次和各個catch代碼塊聲明的異常類型匹配,如果異常對象為某個異常類型或其子類的實例,就執行這個catch代碼塊,不會再執行其他的 catch代碼塊
5) 可嵌套 try-catch-finally 結構。
6) 在 try-catch-finally 結構中,可重新拋出異常。
7) 除了下列情況,總將執行 finally 做為結束:JVM 過早終止(調用 System.exit(int));在 finally 塊中拋出一個未處理的異常;計算機斷電、失火、或遭遇病毒攻擊。

4. try、catch、finally語句塊的執行順序:

1)當try沒有捕獲到異常時:try語句塊中的語句逐一被執行,程序將跳過catch語句塊,執行finally語句塊和其後的語句;

2)當try捕獲到異常,catch語句塊裏沒有處理此異常的情況:當try語句塊裏的某條語句出現異常時,而沒有處理此異常的catch語句塊時,此異常將會拋給JVM處理,finally語句塊裏的語句還是會被執行,但finally語句塊後的語句不會被執行;

3)當try捕獲到異常,catch語句塊裏有處理此異常的情況:在try語句塊中是按照順序來執行的,當執行到某一條語句出現異常時,程序將跳到catch語句塊,並與catch語句塊逐一匹配,找到與之對應的處理程序,其他的catch語句塊將不會被執行,而try語句塊中,出現異常之後的語句也不會被執行,catch語句塊執行完後,執行finally語句塊裏的語句,最後執行finally語句塊後的語句;

 圖示try、catch、finally語句塊的執行順序:


二、 拋出異常

 任何Java代碼都可以拋出異常,如:自己編寫的代碼、來自Java開發環境包中代碼,或者Java運行時係統。無論是誰,都可以通過Java的throw語句拋出異常。從方法中拋出的任何異常都必須使用throws子句。

1、拋出異常throw

Throw在方法內部,用來拋出一個Throwable類型的異常。程序會在throw語句後立即終止,它後麵的語句執行不到,然後在包含它的所有try塊中(可能在上層調用函數中)從裏向外尋找含有與其匹配的catch子句的try塊。

如果拋出了檢查異常,則還應該在方法頭部聲明方法可能拋出的異常類型。該方法的調用者也必須檢查處理拋出的異常。

如果所有方法都層層上拋獲取的異常,最終JVM會進行處理,處理也很簡單,就是打印異常消息和堆棧信息。如果拋出的是Error或RuntimeException,則該方法的調用者可選擇處理該異常。


拋出數組下標越界異常:
  1. public class ExceptionDemo3 {  
  2.     public static void main(String args[]) {    
  3.              
  4.         int[] a = new int[3];  
  5.           
  6.             try {    
  7.                 a[3] = 10;  
  8.             } catch (ArrayIndexOutOfBoundsException e) {    
  9.                 throw new ArrayIndexOutOfBoundsException("數組 下標越界異常了");  
  10.             }   
  11.      }  
  12. }

運行結果:Exception in thread "main"java.lang.ArrayIndexOutOfBoundsException:數組下標越界異常了

    atExceptionDemo3.main(ExceptionDemo3.java:11)

 

throw隻是拋出異常暫時不做處理,但是總有一個地方要處理異常。

2、throws  對異常的說明

 如果一個方法可能會出現異常,但沒有能力處理這種異常,可以在方法聲明處用throws子句來聲明拋出異常。throws語句用在方法定義時聲明該方法要拋出的異常類型,如果拋出的是Exception異常類型,則該方法被聲明為拋出所有的異常,但不推薦直接使用Exception

當方法拋出異常列表的異常時,方法將不對這些類型及其子類類型的異常作處理,而拋向調用該方法的方法,由他去處理。如果調用者不想處理該異常,可以繼續向上拋出,但最終要有能夠處理該異常的調用者。

  Throws拋出異常的規則:

   1) 如果是不可查異常(unchecked exception),即Error、RuntimeException或它們的子類,那麼可以不使用throws關鍵字來聲明要拋出的異常,編譯仍能順利通過,但在運行時會被係統拋出。

   2)必須聲明方法可拋出的任何可查異常(checked exception)。即如果一個方法可能出現受可查異常,要麼用try-catch語句捕獲,要麼用throws子句聲明將它拋出,否則會導致編譯錯誤

   3)僅當拋出了異常,該方法的調用者才必須處理或者重新拋出該異常。當方法的調用者無力處理該異常的時候,應該繼續拋出,而不是囫圇吞棗。

    4)調用方法必須遵循任何可查異常的處理和聲明規則。若覆蓋一個方法,則不能聲明與覆蓋方法不同的異常。聲明的任何異常必須是被覆蓋方法所聲明異常的同類或子類。

三、自定義異常

使用Java內置的異常類可以描述在編程時出現的大部分異常情況。除此之外,用戶還可以自定義異常。用戶自定義異常類,隻需繼承Exception類即可。
    在程序中使用自定義異常類,大體可分為以下幾個步驟。
(1)創建自定義異常類。
(2)在方法中通過throw關鍵字拋出異常對象。
(3)如果在當前拋出異常的方法中處理異常,可以使用try-catch語句捕獲並處理;否則在方法的聲明處通過throws關鍵字指明要拋出給方法調用者的異常,繼續進行下一步操作。
(4)在出現異常方法的調用者中捕獲並處理異常。

自定義的異常:

  1. public class FuShuException extends Exception {  
  2.   
  3.     public FuShuException() {  
  4.         super();      
  5.     }  
  6.   
  7.     public FuShuException(String message) {  
  8.         super(message);  
  9.     }  
  10. }  

  1. public class ExceptionDemo {  
  2.     public static void main(String[] args) {  
  3.       
  4.          int avg;  
  5.         try {  
  6.             avg = getAvg(50,60,-70,80);  
  7.         } catch (FuShuException e) {  
  8.             e.printStackTrace();  
  9.         }  
  10.           
  11.     }  
  12.     //計算成績平均數  
  13.     private static int getAvg(int...source)throws FuShuException {  
  14.         int sum=0;  
  15.         for (int i : source) {  
  16.             if (i<0) {  
  17.                 throw new FuShuException("成績錯誤:"+i);  
  18.             }  
  19.             sum=sum+i;  
  20.         }  
  21.         return sum/source.length;  
  22.     }  

運行結果:

    FuShuException:成績錯誤:-70

    atExceptionDemo.getAvg(ExceptionDemo.java:19)

    atExceptionDemo.main(ExceptionDemo.java:8)



最後更新:2017-11-18 09:33:46

  上一篇:go  效果強悍的博客seo優化秘籍
  下一篇:go  剖析url中漢字對網站優化結果的影響