Java異常處理總結Exception\Error

Java異常處理總結Exception\Error  

2012-12-28 08:17:17|  分類: JAVA |  標籤:java  |舉報|字號 訂閱html

 

 

Java異常處理總結
 
        異常處理是程序設計中一個很是重要的方面,也是程序設計的一大難點,從C開始,你也許已經知道如何用if...else...來控制異常了,也許是自發的,然而這種控制異常痛苦,同一個異常或者錯誤若是多個地方出現,那麼你每一個地方都要作相同處理,感受至關的麻煩!
        Java語言在設計的當初就考慮到這些問題,提出異常處理的框架的方案,全部的異常均可以用一個類型來表示,不一樣類型的異常對應不一樣的子類異常(這裏的異常包括錯誤概念),定義異常處理的規範,在1.4版本之後增長了異常鏈機制,從而便於跟蹤異常!這是Java語言設計者的高明之處,也是Java語言中的一個難點,下面是我對Java異常知識的一個總結,也算是資源回收一下。

1、Java異常的基礎知識
 
        異常是程序中的一些錯誤,但並非全部的錯誤都是異常,而且錯誤有時候是能夠避免的。好比說,你的代碼少了一個分號,那麼運行出來結果是提示是錯誤java.lang.Error;若是你用System.out.println(11/0),那麼你是由於你用0作了除數,會拋出java.lang.ArithmeticException的異常。
        有些異常須要作處理,有些則不須要捕獲處理,後面會詳細講到。
        天有不測風雲,人有旦夕禍福,Java的程序代碼也如此。在編程過程當中,首先應當儘量去避免錯誤和異常發生,對於不可避免、不可預測的狀況則在考慮異常發生時如何處理。
        Java中的異經常使用對象來表示。Java對異常的處理是按異常分類處理的,不一樣異常有不一樣的分類,每種異常都對應一個類型(class),每一個異常都對應一個異常(類的)對象。
        異常類從哪裏來?有兩個來源,一是Java語言自己定義的一些基本異常類型,二是用戶經過繼承Exception類或者其子類本身定義的異常。Exception 類及其子類是 Throwable 的一種形式,它指出了合理的應用程序想要捕獲的條件。
        異常的對象從哪裏來呢?有兩個來源,一是Java運行時環境自動拋出系統生成的異常,而無論你是否願意捕獲和處理,它總要被拋出!好比除數爲0的異常。二是程序員本身拋出的異常,這個異常能夠是程序員本身定義的,也能夠是Java語言中定義的,用throw 關鍵字拋出異常,這種異經常用來向調用者彙報異常的一些信息。
        異常是針對方法來講的,拋出、聲明拋出、捕獲和處理異常都是在方法中進行的。
        Java異常處理經過5個關鍵字try、catch、throw、throws、finally進行管理。基本過程是用try語句塊包住要監視的語句,若是在try語句塊內出現異常,則異常會被拋出,你的代碼在catch語句塊中能夠捕獲到這個異常並作處理;還有以部分系統生成的異常在Java運行時自動拋出。你也能夠經過throws關鍵字在方法上聲明該方法要拋出異常,而後在方法內部經過throw拋出異常對象。finally語句塊會在方法執行return以前執行,通常結構以下:
 try{
  程序代碼
 }catch(異常類型1 異常的變量名1){
  程序代碼
 }catch(異常類型2 異常的變量名2){
  程序代碼
 }finally{
  程序代碼
 }
        catch語句能夠有多個,用來匹配多個異常,匹配上多箇中一個後,執行catch語句塊時候僅僅執行匹配上的異常。catch的類型是Java語言中定義的或者程序員本身定義的,表示代碼拋出異常的類型,異常的變量名錶示拋出異常的對象的引用,若是catch捕獲並匹配上了該異常,那麼就能夠直接用這個異常變量名,此時該異常變量名指向所匹配的異常,而且在catch代碼塊中能夠直接引用。這一點很是很是的特殊和重要!
        Java異常處理的目的是提升程序的健壯性,你能夠在catch和finally代碼塊中給程序一個修正機會,使得程序不因異常而終止或者流程發生之外的改變。同時,經過獲取Java異常信息,也爲程序的開發維護提供了方便,通常經過異常信息就很快就能找到出現異常的問題(代碼)所在。
        Java異常處理是Java語言的一大特點,也是個難點,掌握異常處理可讓寫的代碼更健壯和易於維護。
 
2、Java異常類類圖
 
下面是這幾個類的層次圖:
java.lang.Object
  java.lang.Throwable
      java.lang.Exception
       java.lang.RuntimeException
   java.lang.Error
       java.lang.ThreadDeath
 
下面四個類的介紹來自java api 文檔。
 
一、Throwable
        Throwable 類是 Java 語言中全部錯誤或異常的超類。只有當對象是此類(或其子類之一)的實例時,才能經過 Java 虛擬機或者 Java throw 語句拋出。相似地,只有此類或其子類之一才能夠是 catch 子句中的參數類型。
兩個子類的實例,Error 和 Exception,一般用於指示發生了異常狀況。一般,這些實例是在異常狀況的上下文中新近建立的,所以包含了相關的信息(好比堆棧跟蹤數據)。
 
二、Exception
        Exception 類及其子類是 Throwable 的一種形式,它指出了合理的應用程序想要捕獲的條件,表示程序自己能夠處理的異常
 
三、Error
        Error 是 Throwable 的子類,表示僅靠程序自己沒法恢復的嚴重錯誤,用於指示合理的應用程序不該該試圖捕獲的嚴重問題。
在執行該方法期間,無需在方法中經過throws聲明可能拋出但沒有捕獲的 Error 的任何子類,由於Java編譯器不去檢查它,也就是說,當程序中可能出現這類異常時,即便沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,仍是會編譯經過。
 
四、RuntimeException
        RuntimeException 是那些可能在 Java 虛擬機正常運行期間拋出的異常的超類。Java編譯器不去檢查它,也就是說,當程序中可能出現這類異常時,即便沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,仍是會編譯經過,這種異常能夠經過改進代碼實現來避免。
 
五、ThreadDeath
        調用 Thread 類中帶有零參數的 stop 方法時,受害線程將拋出一個 ThreadDeath 實例。
        僅當應用程序在被異步終止後必須清除時才應該捕獲這個類的實例。若是 ThreadDeath 被一個方法捕獲,那麼將它從新拋出很是重要,由於這樣才能讓該線程真正終止。
若是沒有捕獲 ThreadDeath,則頂級錯誤處理程序不會輸出消息。
        雖然 ThreadDeath 類是「正常出現」的,但它只能是 Error 的子類而不是 Exception 的子類,由於許多應用程序捕獲全部出現的 Exception,而後又將其放棄。
 
        以上是對有關異常API的一個簡單介紹,用法都很簡單,關鍵在於理解異常處理的原理,具體用法參看Java API文檔。
 
3、Java異常處理機制

        對於可能出現異常的代碼,有兩種處理辦法:
        第1、在方法中用try...catch語句捕獲並處理異常,catach語句能夠有多個,用來匹配多個異常。例如:
public void p(int x){
 try{
  ...
 }catch(Exception e){
  ...
 }finally{
  ...
 }
}
 
第2、對於處理不了的異常或者要轉型的異常,在方法的聲明處經過throws語句拋出異常。例如:
public void test1() throws MyException{
 ...
 if(....){
  throw new MyException();
 }
        若是每一個方法都是簡單的拋出異常,那麼在方法調用方法的多層嵌套調用中,Java虛擬機會從出現異常的方法代碼塊中往回找,直到找處處理該異常的代碼塊爲止。而後將異常交給相應的catch語句處理。若是Java虛擬機追溯到方法調用棧最底部main()方法時,若是仍然沒有找處處理異常的代碼塊,將按照下面的步驟處理:
        第1、調用異常的對象的printStackTrace()方法,打印方法調用棧的異常信息。
        第2、若是出現異常的線程爲主線程,則整個程序運行終止;若是非主線程,則終止該線程,其餘線程繼續運行。
        經過分析思考能夠看出,越早處理異常消耗的資源和時間越小,產生影響的範圍也越小。所以,不要把本身能處理的異常也拋給調用者。
        還有一點,不可忽視:finally語句在任何狀況下都必須執行的代碼,這樣能夠保證一些在任何狀況下都必須執行代碼的可靠性。好比,在數據庫查詢異常的時候,應該釋放JDBC鏈接等等。finally語句先於return語句執行,而不論其前後位置,也無論是否try塊出現異常。finally語句惟一不被執行的狀況是方法執行了System.exit()方法。System.exit()的做用是終止當前正在運行的 Java 虛擬機。finally語句塊中不能經過給變量賦新值來改變return的返回值,也建議不要在finally塊中使用return語句,沒有意義還容易致使錯誤。
 
        最後還應該注意一下異常處理的語法規則:
        第1、try語句不能單獨存在,能夠和catch、finally組成 try...catch...finally、try...catch、try...finally三種結構,catch語句能夠有一個或多個,finally語句最多一個,try、catch、finally這三個關鍵字均不能單獨使用。
        第2、try、catch、finally三個代碼塊中變量的做用域分別獨立而不能相互訪問。若是要在三個塊中均可以訪問,則須要將變量定義到這些塊的外面。
        第3、多個catch塊時候,Java虛擬機會匹配其中一個異常類或其子類,就執行這個catch塊,而不會再執行別的catch塊。
        第4、throw語句後不容許有緊跟其餘語句,由於這些沒有機會執行。
        第5、若是一個方法調用了另一個聲明拋出異常的方法,那麼這個方法要麼處理異常,要麼聲明拋出。
 
        那怎麼判斷一個方法可能會出現異常呢?通常來講,方法聲明的時候用了throws語句,方法中有throw語句,方法調用的方法聲明有throws關鍵字。
 
        throw和throws關鍵字的區別
        throw用來拋出一個異常,在方法體內。語法格式爲:throw 異常對象。
        throws用來聲明方法可能會拋出什麼異常,在方法名後,語法格式爲:throws 異常類型1,異常類型2...異常類型n。
 

4、如何定義和使用異常類
 
一、使用已有的異常類,假如爲IOException、SQLException。
 try{
  程序代碼
 }catch(IOException ioe){
  程序代碼
 }catch(SQLException sqle){
  程序代碼
 }finally{
  程序代碼
 }
 
2、自定義異常類
建立Exception或者RuntimeException的子類便可獲得一個自定義的異常類。例如:
 public class MyException extends Exception{
  public MyException(){}
  public MyException(String smg){
   super(smg);
  }
 }
 
三、使用自定義的異常
用throws聲明方法可能拋出自定義的異常,並用throw語句在適當的地方拋出自定義的異常。例如:
在某種條件拋出異常
public void test1() throws MyException{
 ...
 if(....){
  throw new MyException();
 }
}
 
將異常轉型(也叫轉譯),使得異常更易讀易於理解
public void test2() throws MyException{
 ...
 try{
  ...
 }catch(SQLException e){
  ...
  throw new MyException();
 }
}
 
還有一個代碼,頗有意思:
public void test2() throws MyException{
 ...
 try {
  ...
 } catch (MyException e) {
  throw e;
 } 
}

這段代碼實際上捕獲了異常,而後又和盤托出,沒有一點意義,若是這樣還有什麼好處理的,不處理就好了,直接在方法前用throws聲明拋出不就得了。異常的捕獲就要作一些有意義的處理。
 
5、運行時異常和受檢查異常

Exception類能夠分爲兩種:運行時異常和受檢查異常。
一、運行時異常
RuntimeException類及其子類都被稱爲運行時異常,這種異常的特色是Java編譯器不去檢查它,也就是說,當程序中可能出現這類異常時,即便沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,仍是會編譯經過。例如,當除數爲零時,就會拋出java.lang.ArithmeticException異常。
二、受檢查異常
除了RuntimeException類及其子類外,其餘的Exception類及其子類都屬於受檢查異常,這種異常的特色是要麼用try...catch捕獲處理,要麼用throws語句聲明拋出,不然編譯不會經過。
三、二者的區別
運行時異常表示沒法讓程序恢復運行的異常,致使這種異常的緣由一般是因爲執行了錯誤的操做。一旦出現錯誤,建議讓程序終止。
受檢查異常表示程序能夠處理的異常。若是拋出異常的方法自己不處理或者不能處理它,那麼方法的調用者就必須去處理該異常,不然調用會出錯,連編譯也沒法經過。固然,這兩種異常都是能夠經過程序來捕獲並處理的,好比除數爲零的運行時異常:
public class HelloWorld {
 public static void main(String[] args) {
  System.out.println("Hello World!!!"); 
  try{
   System.out.println(1/0);
  }catch(ArithmeticException e){
   System.out.println("除數爲0!");
  }
  System.out.println("除數爲零後程序沒有終止啊,呵呵!!!");  
 }
}
 
運行結果:

Hello World!!!
除數爲0!
除數爲零後程序沒有終止啊,呵呵!!!
 
四、運行時錯誤
Error類及其子類表示運行時錯誤,一般是由Java虛擬機拋出的,JDK中與定義了一些錯誤類,好比VirtualMachineError
和OutOfMemoryError,程序自己沒法修復這些錯誤.通常不去擴展Error類來建立用戶自定義的錯誤類。而RuntimeException類表示程序代碼中的錯誤,是可擴展的,用戶能夠建立特定運行時異常類。
Error(運行時錯誤)和運行時異常的相同之處是:Java編譯器都不去檢查它們,當程序運行時出現它們,都會終止運行。

五、最佳解決方案
        對於運行時異常,咱們不要用try...catch來捕獲處理,而是在程序開發調試階段,儘可能去避免這種異常,一旦發現該異常,正確的作法就會改進程序設計的代碼和實現方式,修改程序中的錯誤,從而避免這種異常。捕獲並處理運行時異常是好的解決辦法,由於能夠經過改進代碼實現來避免該種異常的發生。
        對於受檢查異常,沒說的,老老實實去按照異常處理的方法去處理,要麼用try...catch捕獲並解決,要麼用throws拋出!
對於Error(運行時錯誤),不須要在程序中作任何處理,出現問題後,應該在程序在外的地方找問題,而後解決。
 
6、異常轉型和異常鏈
異常轉型在上面已經提到過了,實際上就是捕獲到異常後,將異常以新的類型的異常再拋出,這樣作通常爲了異常的信息更直觀!好比:
public void run() throws MyException{
 ...
 try{
  ...
 }catch(IOException e){
  ...
  throw new MyException();
 }finally{
  ...
 }
}
 
        異常鏈,在JDK1.4之後版本中,Throwable類支持異常鏈機制。Throwable 包含了其線程建立時線程執行堆棧的快照。它還包含了給出有關錯誤更多信息的消息字符串。最後,它還能夠包含 cause(緣由):另外一個致使此 throwable 拋出的 throwable。它也稱爲異常鏈 設施,由於 cause 自身也會有 cause,依此類推,就造成了異常鏈,每一個異常都是由另外一個異常引發的。 
        通俗的說,異常鏈就是把原始的異常包裝爲新的異常類,並在新的異常類中封裝了原始異常類,這樣作的目的在於找到異常的根本緣由。

        經過Throwable的兩個構造方法能夠建立自定義的包含異常緣由的異常類型
Throwable(String message, Throwable cause) 
          構造一個帶指定詳細消息和 cause 的新 throwable。 
Throwable(Throwable cause) 
          構造一個帶指定 cause 和 (cause==null ? null :cause.toString())(它一般包含類和 cause 的詳細消息)的詳細消息的新 throwable。 
getCause() 
          返回此 throwable 的 cause;若是 cause 不存在或未知,則返回 null。
initCause(Throwable cause) 
          將此 throwable 的 cause 初始化爲指定值。
        在Throwable的子類Exception中,也有相似的指定異常緣由的構造方法:
Exception(String message, Throwable cause) 
          構造帶指定詳細消息和緣由的新異常。 
Exception(Throwable cause) 
          根據指定的緣由和 (cause==null ? null : cause.toString()) 的詳細消息構造新異常(它一般包含 cause 的類和詳細消息)。 
所以,能夠經過擴展Exception類來構造帶有異常緣由的新的異常類。
 
7、Java異常處理的原則和技巧
 
一、避免過大的try塊,不要把不會出現異常的代碼放到try塊裏面,儘可能保持一個try塊對應一個或多個異常。
二、細化異常的類型,不要無論什麼類型的異常都寫成Excetpion。
三、catch塊儘可能保持一個塊捕獲一類異常,不要忽略捕獲的異常,捕獲到後要麼處理,要麼轉譯,要麼從新拋出新類型的異常。
四、不要把本身能處理的異常拋給別人。
五、不要用try...catch參與控制程序流程,異常控制的根本目的是處理程序的非正常狀況。

六種異常處理的陋習java

你以爲本身是一個Java專家嗎?是否確定本身已經全面掌握了Java的異常處理機制?在下面這段代碼中,你可以迅速找出異常處理的六個問題嗎? 

程序員

1 OutputStreamWriter out = ... 
2 java.sql.Connection conn = ... 
3 try { // ⑸ 
4  Statement stat = conn.createStatement(); 
5  ResultSet rs = stat.executeQuery( 
6   "select uid, name from user"); 
7  while (rs.next()) 
8  { 
9   out.println("ID:" + rs.getString("uid") // ⑹ 
10    ",姓名:" + rs.getString("name")); 
11  } 
12  conn.close(); // ⑶ 
13  out.close(); 
14 } 
15 catch(Exception ex) // ⑵ 
16 { 
17  ex.printStackTrace(); //⑴,⑷ 
18 }


  做爲一個Java程序員,你至少應該可以找出兩個問題。可是,若是你不能找出所有六個問題,請繼續閱讀本文。 

  本文討論的不是Java異常處理的通常性原則,由於這些原則已經被大多數人熟知。咱們要作的是分析各類可稱爲「反例」(anti-pattern)的違背優秀編碼規範的常見壞習慣,幫助讀者熟悉這些典型的反面例子,從而可以在實際工做中敏銳地察覺和避免這些問題。 

  反例之一:丟棄異常 

  代碼:15行-18行。 

  這段代碼捕獲了異常卻不做任何處理,能夠算得上Java編程中的殺手。從問題出現的頻繁程度和禍害程度來看,它也許能夠和C/C++程序的一個惡名遠播的問題相提並論??不檢查緩衝區是否已滿。若是你看到了這種丟棄(而不是拋出)異常的狀況,能夠百分之九十九地確定代碼存在問題(在極少數狀況下,這段代碼有存在的理由,但最好加上完整的註釋,以避免引發別人誤解)。 

  這段代碼的錯誤在於,異常(幾乎)老是意味着某些事情不對勁了,或者說至少發生了某些不尋常的事情,咱們不該該對程序發出的求救信號保持沉默和無動於衷。調用一下printStackTrace算不上「處理異常」。不錯,調用printStackTrace對調試程序有幫助,但程序調試階段結束以後,printStackTrace就不該再在異常處理模塊中擔負主要責任了。 

  丟棄異常的情形很是廣泛。打開JDK的ThreadDeath類的文檔,能夠看到下面這段說明:「特別地,雖然出現ThreadDeath是一種‘正常的情形’,但ThreadDeath類是Error而不是Exception的子類,由於許多應用會捕獲全部的Exception而後丟棄它再也不理睬。」這段話的意思是,雖然ThreadDeath表明的是一種普通的問題,但鑑於許多應用會試圖捕獲全部異常而後不予以適當的處理,因此JDK把ThreadDeath定義成了Error的子類,由於Error類表明的是通常的應用不該該去捕獲的嚴重問題。可見,丟棄異常這一壞習慣是如此常見,它甚至已經影響到了Java自己的設計。 

  那麼,應該怎樣改正呢?主要有四個選擇: 

  一、處理異常。針對該異常採起一些行動,例如修正問題、提醒某我的或進行其餘一些處理,要根據具體的情形肯定應該採起的動做。再次說明,調用printStackTrace算不上已經「處理好了異常」。 

  二、從新拋出異常。處理異常的代碼在分析異常以後,認爲本身不能處理它,從新拋出異常也不失爲一種選擇。 

  三、把該異常轉換成另外一種異常。大多數狀況下,這是指把一個低級的異常轉換成應用級的異常(其含義更容易被用戶瞭解的異常)。 

  四、不要捕獲異常。 

  結論一:既然捕獲了異常,就要對它進行適當的處理。不要捕獲異常以後又把它丟棄,不予理睬。 

  反例之二:不指定具體的異常 

  代碼:15行。 

  許多時候人們會被這樣一種「美妙的」想法吸引:用一個catch語句捕獲全部的異常。最多見的情形就是使用catch(Exception ex)語句。但實際上,在絕大多數狀況下,這種作法不值得提倡。爲何呢? 

  要理解其緣由,咱們必須回顧一下catch語句的用途。catch語句表示咱們預期會出現某種異常,並且但願可以處理該異常。異常類的做用就是告訴Java編譯器咱們想要處理的是哪種異常。因爲絕大多數異常都直接或間接從java.lang.Exception派生,catch(Exception ex)就至關於說咱們想要處理幾乎全部的異常。 

  再來看看前面的代碼例子。咱們真正想要捕獲的異常是什麼呢?最明顯的一個是SQLException,這是JDBC操做中常見的異常。另外一個可能的異常是IOException,由於它要操做OutputStreamWriter。顯然,在同一個catch塊中處理這兩種大相徑庭的異常是不合適的。若是用兩個catch塊分別捕獲SQLException和IOException就要好多了。這就是說,catch語句應當儘可能指定具體的異常類型,而不該該指定涵蓋範圍太廣的Exception類。 

  另外一方面,除了這兩個特定的異常,還有其餘許多異常也可能出現。例如,若是因爲某種緣由,executeQuery返回了null,該怎麼辦?答案是讓它們繼續拋出,即沒必要捕獲也沒必要處理。實際上,咱們不能也不該該去捕獲可能出現的全部異常,程序的其餘地方還有捕獲異常的機會??直至最後由JVM處理。 

  結論二:在catch語句中儘量指定具體的異常類型,必要時使用多個catch。不要試圖處理全部可能出現的異常。 

  反例之三:佔用資源不釋放 

  代碼:3行-14行。 

  異常改變了程序正常的執行流程。這個道理雖然簡單,卻經常被人們忽視。若是程序用到了文件、Socket、JDBC鏈接之類的資源,即便遇到了異常,也要正確釋放佔用的資源。爲此,Java提供了一個簡化這類操做的關鍵詞finally。 

  finally是樣好東西:無論是否出現了異常,Finally保證在try/catch/finally塊結束以前,執行清理任務的代碼老是有機會執行。遺憾的是有些人卻不習慣使用finally。 

  固然,編寫finally塊應當多加當心,特別是要注意在finally塊以內拋出的異常??這是執行清理任務的最後機會,儘可能不要再有難以處理的錯誤。 

  結論三:保證全部資源都被正確釋放。充分運用finally關鍵詞。
web

反例之四:不說明異常的詳細信息 

  代碼:3行-18行。 

  仔細觀察這段代碼:若是循環內部出現了異常,會發生什麼事情?咱們能夠獲得足夠的信息判斷循環內部出錯的緣由嗎?不能。咱們只能知道當前正在處理的類發生了某種錯誤,但卻不能得到任何信息判斷致使當前錯誤的緣由。 

  printStackTrace的堆棧跟蹤功能顯示出程序運行到當前類的執行流程,但只提供了一些最基本的信息,未能說明實際致使錯誤的緣由,同時也不易解讀。 

  所以,在出現異常時,最好可以提供一些文字信息,例如當前正在執行的類、方法和其餘狀態信息,包括以一種更適合閱讀的方式整理和組織printStackTrace提供的信息。 

  結論四:在異常處理模塊中提供適量的錯誤緣由信息,組織錯誤信息使其易於理解和閱讀。 

  反例之五:過於龐大的try塊 

  代碼:3行-14行。 

  常常能夠看到有人把大量的代碼放入單個try塊,實際上這不是好習慣。這種現象之因此常見,緣由就在於有些人圖省事,不肯花時間分析一大塊代碼中哪幾行代碼會拋出異常、異常的具體類型是什麼。把大量的語句裝入單個巨大的try塊就象是出門旅遊時把全部平常用品塞入一個大箱子,雖然東西是帶上了,但要找出來可不容易。 

  一些新手經常把大量的代碼放入單個try塊,而後再在catch語句中聲明Exception,而不是分離各個可能出現異常的段落並分別捕獲其異常。這種作法爲分析程序拋出異常的緣由帶來了困難,由於一大段代碼中有太多的地方可能拋出Exception。 

  結論五:儘可能減少try塊的體積。 

  反例之六:輸出數據不完整 

  代碼:7行-11行。 

  不完整的數據是Java程序的隱形殺手。仔細觀察這段代碼,考慮一下若是循環的中間拋出了異常,會發生什麼事情。循環的執行固然是要被打斷的,其次,catch塊會執行??就這些,再也沒有其餘動做了。已經輸出的數據怎麼辦?使用這些數據的人或設備將收到一份不完整的(於是也是錯誤的)數據,卻得不到任何有關這份數據是否完整的提示。對於有些系統來講,數據不完整可能比系統中止運行帶來更大的損失。 

  較爲理想的處置辦法是向輸出設備寫一些信息,聲明數據的不完整性;另外一種可能有效的辦法是,先緩衝要輸出的數據,準備好所有數據以後再一次性輸出。 

  結論六:全面考慮可能出現的異常以及這些異常對執行流程的影響。 

  改寫後的代碼 

  根據上面的討論,下面給出改寫後的代碼。也許有人會說它稍微有點?嗦,可是它有了比較完備的異常處理機制。 

spring

OutputStreamWriter out = ... 
java.sql.Connection conn = ... 
try { 
 Statement stat = conn.createStatement(); 
 ResultSet rs = stat.executeQuery( 
  "select uid, name from user"); 
 while (rs.next()) 
 { 
  out.println("ID:" + rs.getString("uid") + ",姓名: " + rs.getString("name")); 
 } 

catch(SQLException sqlex) 

 out.println("警告:數據不完整"); 
 throw new ApplicationException("讀取數據時出現SQL錯誤", sqlex); 

catch(IOException ioex) 

 throw new ApplicationException("寫入數據時出現IO錯誤", ioex); 

finally 

 if (conn != null) { 
  try { 
   conn.close(); 
  } 
  catch(SQLException sqlex2) 
  { 
   System.err(this.getClass().getName() + ".mymethod - 不能關閉數據庫鏈接: " + sqlex2.toString()); 
  } 
 } 

 if (out != null) { 
  try { 
   out.close(); 
  } 
  catch(IOException ioex2) 
  { 
   System.err(this.getClass().getName() + ".mymethod - 不能關閉輸出文件" + ioex2.toString()); 
  } 
 } 
}

  本文的結論不是放之四海皆準的教條,有時常識和經驗纔是最好的老師。若是你對本身的作法沒有百分之百的信心,務必加上詳細、全面的註釋。 

  另外一方面,不要笑話這些錯誤,不妨問問你本身是否真地完全擺脫了這些壞習慣。即便最有經驗的程序員偶爾也會誤入歧途,緣由很簡單,由於它們確確實實帶來了「方便」。全部這些反例均可以看做Java編程世界的惡魔,它們美麗動人,無孔不入,時刻誘惑着你。也許有人會認爲這些都屬於雞皮蒜毛的小事,不足掛齒,但請記住:勿以惡小而爲之,勿以善小而不爲。





------------------------------------------------------------------下面是一些java異常集-------------------------------------------------------------------------------------------

 


算術異常類:ArithmeticExecptionsql

空指針異常類:NullPointerException數據庫

類型強制轉換異常:ClassCastExceptionapache

數組負下標異常:NegativeArrayException編程

數組下標越界異常:ArrayIndexOutOfBoundsExceptionapi

違背安全原則異常:SecturityException

文件已結束異常:EOFException

文件未找到異常:FileNotFoundException

字符串轉換爲數字異常:NumberFormatException


操做數據庫異常:SQLException


輸入輸出異常:IOException


方法未找到異常:NoSuchMethodException

java.lang.AbstractMethodError

抽象方法錯誤。當應用試圖調用抽象方法時拋出。

java.lang.AssertionError

斷言錯。用來指示一個斷言失敗的狀況。

java.lang.ClassCircularityError

類循環依賴錯誤。在初始化一個類時,若檢測到類之間循環依賴則拋出該異常。

java.lang.ClassFormatError

類格式錯誤。當Java虛擬機試圖從一個文件中讀取Java類,而檢測到該文件的內容不符合類的有效格式時拋出。

java.lang.Error

錯誤。是全部錯誤的基類,用於標識嚴重的程序運行問題。這些問題一般描述一些不該被應用程序捕獲的反常狀況。

java.lang.ExceptionInInitializerError

初始化程序錯誤。當執行一個類的靜態初始化程序的過程當中,發生了異常時拋出。靜態初始化程序是指直接包含於類中的static語句段。

java.lang.IllegalAccessError

違法訪問錯誤。當一個應用試圖訪問、修改某個類的域(Field)或者調用其方法,可是又違反域或方法的可見性聲明,則拋出該異常。

java.lang.IncompatibleClassChangeError

不兼容的類變化錯誤。當正在執行的方法所依賴的類定義發生了不兼容的改變時,拋出該異常。通常在修改了應用中的某些類的聲明定義而沒有對整個應用從新編譯而直接運行的狀況下,容易引起該錯誤。

java.lang.InstantiationError

實例化錯誤。當一個應用試圖經過Java的new操做符構造一個抽象類或者接口時拋出該異常.

java.lang.InternalError

內部錯誤。用於指示Java虛擬機發生了內部錯誤。

java.lang.LinkageError

連接錯誤。該錯誤及其全部子類指示某個類依賴於另一些類,在該類編譯以後,被依賴的類改變了其類定義而沒有從新編譯全部的類,進而引起錯誤的狀況。

java.lang.NoClassDefFoundError

未找到類定義錯誤。當Java虛擬機或者類裝載器試圖實例化某個類,而找不到該類的定義時拋出該錯誤。

java.lang.NoSuchFieldError

域不存在錯誤。當應用試圖訪問或者修改某類的某個域,而該類的定義中沒有該域的定義時拋出該錯誤。

java.lang.NoSuchMethodError

方法不存在錯誤。當應用試圖調用某類的某個方法,而該類的定義中沒有該方法的定義時拋出該錯誤。

java.lang.OutOfMemoryError

內存不足錯誤。當可用內存不足以讓Java虛擬機分配給一個對象時拋出該錯誤。

java.lang.StackOverflowError

堆棧溢出錯誤。當一個應用遞歸調用的層次太深而致使堆棧溢出時拋出該錯誤。

java.lang.ThreadDeath

線程結束。當調用Thread類的stop方法時拋出該錯誤,用於指示線程結束。

java.lang.UnknownError

未知錯誤。用於指示Java虛擬機發生了未知嚴重錯誤的狀況。

java.lang.UnsatisfiedLinkError

未知足的連接錯誤。當Java虛擬機未找到某個類的聲明爲native方法的本機語言定義時拋出。

java.lang.UnsupportedClassVersionError

不支持的類版本錯誤。當Java虛擬機試圖從讀取某個類文件,可是發現該文件的主、次版本號不被當前Java虛擬機支持的時候,拋出該錯誤。

java.lang.VerifyError

驗證錯誤。當驗證器檢測到某個類文件中存在內部不兼容或者安全問題時拋出該錯誤。

java.lang.VirtualMachineError

虛擬機錯誤。用於指示虛擬機被破壞或者繼續執行操做所需的資源不足的狀況。


java.lang.ArithmeticException

算術條件異常。譬如:整數除零等。

java.lang.ArrayIndexOutOfBoundsException

數組索引越界異常。當對數組的索引值爲負數或大於等於數組大小時拋出。

java.lang.ArrayStoreException

數組存儲異常。當向數組中存放非數組聲明類型對象時拋出。

java.lang.ClassCastException

類造型異常。假設有類A和B(A不是B的父類或子類),O是A的實例,那麼當強制將O構造爲類B的實例時拋出該異常。該異常常常被稱爲強制類型轉換異常。

java.lang.ClassNotFoundException

找不到類異常。當應用試圖根據字符串形式的類名構造類,而在遍歷CLASSPAH以後找不到對應名稱的class文件時,拋出該異常。

java.lang.CloneNotSupportedException

不支持克隆異常。當沒有實現Cloneable接口或者不支持克隆方法時,調用其clone()方法則拋出該異常。

java.lang.EnumConstantNotPresentException

枚舉常量不存在異常。當應用試圖經過名稱和枚舉類型訪問一個枚舉對象,但該枚舉對象並不包含常量時,拋出該異常。

java.lang.Exception

根異常。用以描述應用程序但願捕獲的狀況。

java.lang.IllegalAccessException

違法的訪問異常。當應用試圖經過反射方式建立某個類的實例、訪問該類屬性、調用該類方法,而當時又沒法訪問類的、屬性的、方法的或構造方法的定義時拋出該異常。

java.lang.IllegalMonitorStateException

違法的監控狀態異常。當某個線程試圖等待一個本身並不擁有的對象(O)的監控器或者通知其餘線程等待該對象(O)的監控器時,拋出該異常。

java.lang.IllegalStateException

違法的狀態異常。當在Java環境和應用還沒有處於某個方法的合法調用狀態,而調用了該方法時,拋出該異常。

java.lang.IllegalThreadStateException

違法的線程狀態異常。當縣城還沒有處於某個方法的合法調用狀態,而調用了該方法時,拋出異常。

java.lang.IndexOutOfBoundsException

索引越界異常。當訪問某個序列的索引值小於0或大於等於序列大小時,拋出該異常。

java.lang.InstantiationException

實例化異常。當試圖經過newInstance()方法建立某個類的實例,而該類是一個抽象類或接口時,拋出該異常。

java.lang.InterruptedException

被停止異常。當某個線程處於長時間的等待、休眠或其餘暫停狀態,而此時其餘的線程經過Thread的interrupt方法終止該線程時拋出該異常。

java.lang.NegativeArraySizeException

數組大小爲負值異常。當使用負數大小值建立數組時拋出該異常。

java.lang.NoSuchFieldException

屬性不存在異常。當訪問某個類的不存在的屬性時拋出該異常。

java.lang.NoSuchMethodException

方法不存在異常。當訪問某個類的不存在的方法時拋出該異常。

java.lang.NullPointerException

空指針異常。當應用試圖在要求使用對象的地方使用了null時,拋出該異常。譬如:調用null對象的實例方法、訪問null對象的屬性、計算null對象的長度、使用throw語句拋出null等等。

java.lang.NumberFormatException

數字格式異常。當試圖將一個String轉換爲指定的數字類型,而該字符串確不知足數字類型要求的格式時,拋出該異常。

java.lang.RuntimeException

運行時異常。是全部Java虛擬機正常操做期間能夠被拋出的異常的父類。

java.lang.SecurityException

安全異常。由安全管理器拋出,用於指示違反安全狀況的異常。

java.lang.StringIndexOutOfBoundsException

字符串索引越界異常。當使用索引值訪問某個字符串中的字符,而該索引值小於0或大於等於序列大小時,拋出該異常。

java.lang.TypeNotPresentException

類型不存在異常。當應用試圖以某個類型名稱的字符串表達方式訪問該類型,可是根據給定的名稱又找不到該類型是拋出該異常。該異常與ClassNotFoundException的區別在於該異常是unchecked(不被檢查)異常,而ClassNotFoundException是checked(被檢查)異常。

java.lang.UnsupportedOperationException

不支持的方法異常。指明請求的方法不被支持狀況的異常。

異常
javax.servlet.jsp.JspException: Cannot retrieve mapping for action /Login (/Login是你的action名字)  

可能緣由
action沒有再struts-config.xml 中定義,或沒有找到匹配的action,例如在JSP文件中使用 <html:form action="Login.do".將表單提交給Login.do處理,若是出現上述異常,請查看struts-config.xml中的定義部分,有時多是打錯了字符或者是某些不符合規則,可使用strutsconsole工具來檢查。
-----------------------------------------------------------------------------------------------------------------
異常
org.apache.jasper.JasperException: Cannot retrieve definition for form bean null

可能緣由      
       
這個異常是由於Struts根據struts-config.xml中的mapping沒有找到action指望的form bean。大部分的狀況多是由於在form-bean中設置的name屬性和action中設置的name屬性不匹配所致。換句話說,action和form都應該各自有一個name屬性,而且要精確匹配,包括大小寫。這個錯誤當沒有name屬性和action關聯時也會發生,若是沒有在action中指定name屬性,那麼就沒有name屬性和action相關聯。固然當action製做某些控制時,譬如根據參數值跳轉到相應的jsp頁面,而不是處理表單數據,這是就不用name屬性,這也是action的使用方法之一。
-----------------------------------------------------------------------------------------------------------------
異常
No action instance for path /xxxx could be created

可能緣由
特別提示:由於有不少中狀況會致使這個錯誤的發生,因此推薦你們調高你的web服務器的日誌/調試級別,這樣能夠從更多的信息中看到潛在的、在試圖建立action類時發生的錯誤,這個action類你已經在struts-config.xml中設置了關聯(即添加了<action>標籤)。

在struts-config.xml中經過action標籤的class屬性指定的action類不能被找到有不少種緣由,例如:定位編譯後的.class文件失敗。Failure to place compiled .class file for the action in the classpath (在web開發中,class的的位置在r WEB-INF/classes,因此你的action class必需要在這個目錄下。例如你的action類位於WEB-INF/classes/action/Login.class,那麼在struts-config.xml中設置action的屬性type時就是action.Login).
拼寫錯誤,這個也時有發生,而且不易找到,特別注意第一個字母的大小寫和包的名稱。 
-----------------------------------------------------------------------------------------------------------------
異常
javax.servlet.jsp.JspException: No getter method for property username of bean org.apache.struts.taglib.html.BEAN

可能緣由
沒有位form bean中的某個變量定義getter 方法

這個錯誤主要發生在表單提交的FormBean中,用struts標記<html:text property=」username」>時,在FormBean中必須有一個getUsername()方法。注意字母「U」。
-----------------------------------------------------------------------------------------------------------------
異常
java.lang.NoClassDefFoundError: org/apache/struts/action/ActionForm

可能緣由
這個錯誤主要發生在在classpath中找不到相應的Java .class文件。若是這個錯誤發生在web應用程序的運行時,主要是由於指定的class文件不在web server的classpath中(/WEB-INF/classes 和 /WEB-INF/lib)。在上面的錯誤中,緣由是找不到ActionForm類。
-----------------------------------------------------------------------------------------------------------------
異常
javax.servlet.jsp.JspException: Exception creating bean of class org.apache.struts.action.ActionForm: {1}

可能緣由
Instantiating Struts-provided ActionForm class directly instead of instantiating a class derived off ActionForm. This mightoccur implicitly if you specify that a form-bean is this Struts ActionForm class rather than specifying a child of this classfor the form-bean.

Not associating an ActionForm-descended class with an action can also lead to this error.
-----------------------------------------------------------------------------------------------------------------
異常
javax.servlet.jsp.JspException: Cannot find ActionMappings or ActionFormBeans collection

可能緣由
不是標識Struts actionServlet的<servlet>標記就是映射.do擴展名的<sevlet-mapping>標記或者二者都沒有在web.xml中聲明。

在struts-config.xml中的打字或者拼寫錯誤也可致使這個異常的發生。例如缺乏一個標記的關閉符號/>。最好使用struts console工具檢查一下。

另外,load-on-startup必須在web.xml中聲明,這要麼是一個空標記,要麼指定一個數值,這個數值用來表servlet運行的優先級,數值越大優先級越低。

還有一個和使用load-on-startup有關的是使用Struts預編譯JSP文件時也可能致使這個異常。
-----------------------------------------------------------------------------------------------------------------
異常
java.lang.NullPointerException at org.apache.struts.util.RequestUtils.forwardURL(RequestUtils.java:1223)

可能緣由
在struts-config.xml中的forward元素缺乏path屬性。例如應該是以下形式:
<forward name="userhome" path="/user/userhome.jsp"/>
-----------------------------------------------------------------------------------------------------------------
異常
javax.servlet.jsp.JspException: Cannot find bean org.apache.struts.taglib.html.BEAN in any scope


 

Probable Causes
試圖在Struts的form標記外使用form的子元素。這經常發生在你在</html:form>後面使用Struts的html標記。另外要注意可能你不經意使用的無主體的標記,如<html:form … />,這樣web 服務器解析時就看成一個無主體的標記,隨後使用的全部<html>標記都被認爲是在這個標記以外的,如又使用了<html:text property=」id」>還有就是在使用taglib引入HTML標記庫時,你使用的prefix的值不是html。
-----------------------------------------------------------------------------------------------------------------
異常
javax.servlet.jsp.JspException: Missing message for key xx.xx.xx

Probable Causes
這個key的值對沒有在資源文件ApplicationResources.properties中定義。若是你使用eclipse時常常碰到這樣的狀況,當項目從新編譯時,eclipse會自動將classes目錄下的資源文件刪除。

資源文件ApplicationResources.properties 不在classpath中應將資源文件放到 WEB-INF/classes 目錄下,固然要在struts-config.xml中定義)
-----------------------------------------------------------------------------------------------------------------
異常
Cannot find message resources under key org.apache.struts.action.MESSAGE

可能緣由
很顯然,這個錯誤是發生在使用資源文件時,而Struts沒有找到資源文件。

Implicitly trying to use message resources that are not available (such as using empty html:options tag instead of specifyingthe options in its body -- this assumes options are specified in ApplicationResources.properties file)

XML parser issues -- too many, too few, incorrect/incompatible versions
-----------------------------------------------------------------------------------------------------------------
異常
Strange and seemingly random characters in HTML and on screen, but not in original JSP or servlet.

可能緣由
混和使用Struts的html:form標記和標準的HTML標記不正確。

使用的編碼樣式在本頁中不支持。
-----------------------------------------------------------------------------------------------------------------
異常
"Document contained no data" in Netscape

No data rendered (completely empty) page in Microsoft Internet Explorer

可能緣由
使用一個Action的派生類而沒有實現perform()方法或execute()方法。在Struts1.0中實現的是perform()方法,在Struts1.1中實現的是execute()方法,但Struts1.1向後兼容perform()方法。但你使用Struts1.1建立一個Action的派生類,而且實現了execute()方法,而你在Struts1.0中運行的話,就會獲得"Document contained nodata" error message in Netscape or a completely empty (no HTML whatsoever) page rendered in Microsoft Internet Explorer.」的錯誤信息。

---------------------------------------------------------------------------------------------------------------------------異常ServletException: BeanUtils.populate解決方案在用Struts上傳文件時,遇到了javax.servlet.ServletException: BeanUtils.populate異常。個人ActionServlet並無用到BeanUtils這些工具類。後來仔細檢查代碼發現是在jsp文件裏的form忘了加enctype=&quot;multipart/form-data&quot; 了。因此寫程序遇到錯誤或異常應該從多方面考慮問題存在的可能性,想到系統提示信息之外的東西。----------------------------------------------------------------------------------------------------------------------------1. 定義Action後, 若是指定了name, 那麼必需要定義一個與它同名的FormBean才能進行form映射.2. 若是定義Action後, 提交頁面時出現 "No input attribute for mapping path..." 錯誤, 則須要在其input屬性中定義轉向的頁面.3. 若是插入新的數據時出現 "Batch update row count wrong:..." 錯誤, 則說明XXX.hbm.xml中指定的key的類型爲原始類型(int, long),由於這種類型會自動分配值, 而這個值每每會讓系統認爲已經存在該記錄, 正確的方法是使用java.lang.Integer或java.lang.Long對象.4. 若是插入數據時出現 "argument type mismatch" 錯誤, 多是你使用了Date等特殊對象, 由於struts不能自動從String型轉換成Date型,因此, 你須要在Action中手動把String型轉換成Date型.5. Hibernate中, Query的iterator()比list()方法快不少.6. 若是出現 "equal symbol expected" 錯誤, 說明你的strtus標籤中包含另外一個標籤或者變量, 例如:<html:select property="test" onchange="<%=test%>"/>或者<html:hidden property="test" value="<bean:write name="t" property="p"/>"/>這樣的狀況... ---------------------------------------------------------------------------------------------------------------------------錯誤:Exception in thread "main" org.hibernate.exception.SQLGrammarException: Could not execute JDBC batch update緣由與解決:      由於Hibernate Tools(或者Eclipse自己的Database Explorer)生成*.hbn.xml工具中包含有catalog="***"(*表示數據庫名稱)這樣的屬性,將該屬性刪除就能夠了---------------------------------------------------------------------------------------------------------------------------錯誤:org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations)緣由與解決:方法1 刪除Set方的cascade方法2 解決關聯關係後,再刪除方法3 在many-to-one方增長cascade 但值不能是none最後一招:檢查一下hashCode equals是否使用了id做爲惟一標示的選項了;我用uuid.hex時是沒有問題的;可是用了native,就不行了,怎麼辦?刪除啊!----------------------------------------------------------------------------------------------------------------------------問題:今天用Tomcat 5.5.12,發現原來很好用的系統不能用了,反覆測試發現頁面中不能包含 taglib,不然會出現如下提示:HTTP Status 500 -type Exception reportMessage description The server encountered an internal error () that prevented it from fulfilling this request.exceptionorg.apache.jasper.JasperException: /index.jsp(1,1) Unable to read TLD "META-INF/tlds/struts-bean.tld" from JAR file"file:*****/WEB-INF/lib/struts.jar":緣由:更新了工程用的lib文件夾下的jar,發佈時也發佈了servlet.jar和jsp-api.jar。解決:把jsp-api.jar刪除就解決這個問題了。-----------------------------------------------------------------------------------------------------------------------------錯誤: java.lang.NullPointerException緣由: 發現 dao 實例、 manage 實例等須要注入的東西沒有被注入(俗稱空指針異常)解決:這個時候,你應該查看日誌文件;默認是應用服務器的 log 文件,好比 Tomcat 就是 [Tomcat 安裝目錄 ]/logs ;你會發現提示你:多是:org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sf' defined in ServletContextresource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception isorg.hibernate.HibernateException: could not configure from URL: file:src/hibernate.cfg.xmlorg.hibernate.HibernateException: could not configure from URL: file:src/hibernate.cfg.xml……………………….Caused by: java.io.FileNotFoundException: src\hibernate.cfg.xml多是:org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined inServletContext resource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception isorg.hibernate.MappingException: Resource: com/mcc/coupon/model/UserRole.hbm.xml not foundorg.hibernate.MappingException: Resource: com/mcc/coupon/model/UserRole.hbm.xml not found而後你就知道緣由是由於配置文件的解析出了錯誤,這個經過 Web 頁面是看不出來的。更多的是持久化影射文件出的錯誤;致使了沒有被解析;固然你須要的功能就沒法使用了。----------------------------------------------------------------------------------------------------------------------------錯誤:StandardWrapperValve[action]: Servlet.service() for servlet action threw exceptionjavax.servlet.jsp.JspException: Cannot retrieve mapping for action /settlementTypeManage或者:      type Status report      message Servlet action is not available      description The requested resource (Servlet action is not available) is not available.緣由: 同 上----------------------------------------------------------------------------------------------------------------------------錯誤StandardWrapperValve[jsp]: Servlet.service() for servlet jsp threw exceptionjava.lang.ClassNotFoundException: org.apache.struts.taglib.bean.CookieTei界面錯誤具體描述:org.apache.jasper.JasperException: Failed to load or instantiate TagExtraInfo class: org.apache.struts.taglib.bean.CookieTei      緣由與解決:    <方案一>你的「html:」開頭的標籤沒有放在一個<html:form>中       <方案二>從新啓動你的應用服務器,自動就沒有這個問題

相關文章
相關標籤/搜索