熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

Java異常處理

2013-11-23 18:57:58  來源: Java核心技術 

  異常處理是程序設計中一個非常重要的方面也是程序設計的一大難點從C開始你也許已經知道如何用if……else……來控制異常了也許是自發的然而這種控制異常痛苦同一個異常或者錯誤如果多個地方出現那麼你每個地方都要做相同處理感覺相當的麻煩!

  Java語言在設計的當初就考慮到這些問題提出異常處理的框架的方案所有的異常都可以用一個類型來表示不同類型的異常對應不同的子類異常(這裡的異常包括錯誤概念)定義異常處理的規范版本以後增加了異常鏈機制從而便於跟蹤異常!這是Java語言設計者的高明之處也是Java語言中的一個難點下面是我對Java異常知識的一個總結也算是資源回收一下

  一Java異常的基礎知識

  異常是程序中的一些錯誤但並不是所有的錯誤都是異常並且錯誤有時候是可以避免的比如說你的代碼少了一個分號那麼運行出來結果是提示是錯誤javalangError如果你用Systemoutprintln(/那麼你是因為你用做了除數會拋出javalangArithmeticException的異常

  有些異常需要做處理有些則不需要捕獲處理後面會詳細講到

  天有不測風雲人有旦夕禍福Java的程序代碼也如此在編程過程中首先應當盡可能去避免錯誤和異常發生對於不可避免不可預測的情況則在考慮異常發生時如何處理

  Java中的異常用對象來表示Java對異常的處理是按異常分類處理的不同異常有不同的分類每種異常都對應一個類型(class)每個異常都對應一個異常(類的)對象

  異常類從哪裡來?有兩個來源一是Java語言本身定義的一些基本異常類型二是用戶通過繼承Exception類或者其子類自己定義的異常Exception 類及其子類是 Throwable 的一種形式它指出了合理的應用程序想要捕獲的條件

  異常的對象從哪裡來呢?有兩個來源一是Java運行時環境自動拋出系統生成的異常而不管你是否願意捕獲和處理它總要被拋出!比如除數為的異常二是程序員自己拋出的異常這個異常可以是程序員自己定義的也可以是Java語言中定義的用throw 關鍵字拋出異常這種異常常用來向調用者匯報異常的一些信息

  異常是針對方法來說的拋出聲明拋出捕獲和處理異常都是在方法中進行的

  Java異常處理通過個關鍵字trycatchthrowthrowsfinally進行管理基本過程是用try語句塊包住要監視的語句如果在try語句塊內出現異常則異常會被拋出你的代碼在catch語句塊中可以捕獲到這個異常並做處理還有以部分系統生成的異常在Java運行時自動拋出你也可以通過throws關鍵字在方法上聲明該方法要拋出異常然後在方法內部通過throw拋出異常對象finally語句塊會在方法執行return之前執行一般結構如下try{程序代碼}catch(異常類型 異常的變量名){程序代碼}catch(異常類型 異常的變量名){程序代碼}finally{程序代碼} catch語句可以有多個用來匹配多個異常匹配上多個中一個後執行catch語句塊時候僅僅執行匹配上的異常catch的類型是Java語言中定義的或者程序員自己定義的表示代碼拋出異常的類型異常的變量名表示拋出異常的對象的引用如果catch捕獲並匹配上了該異常那麼就可以直接用這個異常變量名此時該異常變量名指向所匹配的異常並且在catch代碼塊中可以直接引用這一點非常非常的特殊和重要!

  Java異常處理的目的是提高程序的健壯性你可以在catch和finally代碼塊中給程序一個修正機會使得程序不因異常而終止或者流程發生以外的改變同時通過獲取Java異常信息也為程序的開發維護提供了方便一般通過異常信息就很快就能找到出現異常的問題(代碼)所在

  Java異常處理是Java語言的一大特色也是個難點掌握異常處理可以讓寫的代碼更健壯和易於維護

  二Java異常類類圖

  下面是這幾個類的層次圖javalangObject javalangThrowable javalangException javalangRuntimeException javalangError javalangThreadDeath

  下面四個類的介紹來自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文檔

  三Java異常處理機制

  對於可能出現異常的代碼有兩種處理辦法第一在方法中用try……catch語句捕獲並處理異常catach語句可以有多個用來匹配多個異常例如public void p(int x){ try{……

  }catch(Exception e){……

  }finally{……

  }

  第二對於處理不了的異常或者要轉型的異常在方法的聲明處通過throws語句拋出異常例如public void test() throws MyException{……

  if(……){ throw new MyException()}如果每個方法都是簡單的拋出異常那麼在方法調用方法的多層嵌套調用中Java虛擬機會從出現異常的方法代碼塊中往回找直到找到處理該異常的代碼塊為止然後將異常交給相應的catch語句處理如果Java虛擬機追溯到方法調用棧最底部main()方法時如果仍然沒有找到處理異常的代碼塊將按照下面的步驟處理第一調用異常的對象的printStackTrace()方法打印方法調用棧的異常信息

  第二如果出現異常的線程為主線程則整個程序運行終止如果非主線程則終止該線程其他線程繼續運行

  通過分析思考可以看出越早處理異常消耗的資源和時間越小產生影響的范圍也越小因此不要把自己能處理的異常也拋給調用者

  還有一點不可忽視finally語句在任何情況下都必須執行的代碼這樣可以保證一些在任何情況下都必須執行代碼的可靠性比如在數據庫查詢異常的時候應該釋放JDBC連接等等finally語句先於return語句執行而不論其先後位置也不管是否try塊出現異常finally語句唯一不被執行的情況是方法執行了Systemexit()方法Systemexit()的作用是終止當前正在運行的 Java 虛擬機finally語句塊中不能通過給變量賦新值來改變return的返回值也建議不要在finally塊中使用return語句沒有意義還容易導致錯誤

  最後還應該注意一下異常處理的語法規則第一try語句不能單獨存在可以和catchfinally組成 try……catch……finallytry……catchtry……finally三種結構catch語句可以有一個或多個finally語句最多一個trycatchfinally這三個關鍵字均不能單獨使用

  第二trycatchfinally三個代碼塊中變量的作用域分別獨立而不能相互訪問如果要在三個塊中都可以訪問則需要將變量定義到這些塊的外面

  第三多個catch塊時候Java虛擬機會匹配其中一個異常類或其子類就執行這個catch塊而不會再執行別的catch塊

  第四throw語句後不允許有緊跟其他語句因為這些沒有機會執行

  第五如果一個方法調用了另外一個聲明拋出異常的方法那麼這個方法要麼處理異常要麼聲明拋出

  那怎麼判斷一個方法可能會出現異常呢?一般來說方法聲明的時候用了throws語句方法中有throw語句方法調用的方法聲明有throws關鍵字

  throw和throws關鍵字的區別throw用來拋出一個異常在方法體內語法格式為throw 異常對象

  throws用來聲明方法可能會拋出什麼異常在方法名後語法格式為throws 異常類型異常類型……異常類型n

  四如何定義和使用異常類

  使用已有的異常類假如為IOExceptionSQLException try{程序代碼}catch(IOException ioe){程序代碼}catch(SQLException sqle){程序代碼}finally{程序代碼}

  自定義異常類創建Exception或者RuntimeException的子類即可得到一個自定義的異常類例如public class MyException extends Exception{ public MyException(){} public MyException(String smg){ super(smg)}

  使用自定義的異常用throws聲明方法可能拋出自定義的異常並用throw語句在適當的地方拋出自定義的異常例如在某種條件拋出異常public void test() throws MyException{……

  if(……){ throw new MyException()}

  將異常轉型(也叫轉譯)使得異常更易讀易於理解public void test() throws MyException{……

  try{……

  }catch(SQLException e){……

  throw new MyException()}

  還有一個代碼很有意思public void test() throws MyException{……

  try {……

  } catch (MyException e) { throw e}

  這段代碼實際上捕獲了異常然後又和盤托出沒有一點意義如果這樣還有什麼好處理的不處理就行了直接在方法前用throws聲明拋出不就得了異常的捕獲就要做一些有意義的處理

  五運行時異常和受檢查異常

  Exception類可以分為兩種運行時異常和受檢查異常

  運行時異常RuntimeException類及其子類都被稱為運行時異常這種異常的特點是Java編譯器不去檢查它也就是說當程序中可能出現這類異常時即使沒有用try……catch語句捕獲它也沒有用throws字句聲明拋出它還是會編譯通過例如當除數為零時就會拋出javalangArithmeticException異常

  受檢查異常除了RuntimeException類及其子類外其他的Exception類及其子類都屬於受檢查異常這種異常的特點是要麼用try……catch捕獲處理要麼用throws語句聲明拋出否則編譯不會通過

  兩者的區別運行時異常表示無法讓程序恢復運行的異常導致這種異常的原因通常是由於執行了錯誤的操作一旦出現錯誤建議讓程序終止

  受檢查異常表示程序可以處理的異常如果拋出異常的方法本身不處理或者不能處理它那麼方法的調用者就必須去處理該異常否則調用會出錯連編譯也無法通過當然這兩種異常都是可以通過程序來捕獲並處理的比如除數為零的運行時異常public class HelloWorld { public static void main(String[] args) { Systemoutprintln(Hello World!!!try{ Systemoutprintln(/}catch(ArithmeticException e){ Systemoutprintln(除數為} Systemoutprintln(除數為零後程序沒有終止啊呵呵!!!}

  運行結果

  Hello World!!!

  除數為

  除數為零後程序沒有終止啊呵呵!!!

  運行時錯誤Error類及其子類表示運行時錯誤通常是由Java虛擬機拋出的JDK中與定義了一些錯誤類比如VirtualMachineError和OutOfMemoryError程序本身無法修復這些錯誤一般不去擴展Error類來創建用戶自定義的錯誤類而RuntimeException類表示程序代碼中的錯誤是可擴展的用戶可以創建特定運行時異常類

  Error(運行時錯誤)和運行時異常的相同之處是Java編譯器都不去檢查它們當程序運行時出現它們都會終止運行

  最佳解決方案對於運行時異常我們不要用try……catch來捕獲處理而是在程序開發調試階段盡量去避免這種異常一旦發現該異常正確的做法就會改進程序設計的代碼和實現方式修改程序中的錯誤從而避免這種異常捕獲並處理運行時異常是好的解決辦法因為可以通過改進代碼實現來避免該種異常的發生

  對於受檢查異常沒說的老老實實去按照異常處理的方法去處理要麼用try……catch捕獲並解決要麼用throws拋出!

  對於Error(運行時錯誤)不需要在程序中做任何處理出現問題後應該在程序在外的地方找問題然後解決

  六異常轉型和異常鏈異常轉型在上面已經提到過了實際上就是捕獲到異常後將異常以新的類型的異常再拋出這樣做一般為了異常的信息更直觀!比如public void run() throws MyException{……

  try{……

  }catch(IOException e){……

  throw new MyException()}finally{……

  }

  異常鏈在JDK以後版本中Throwable類支持異常鏈機制Throwable 包含了其線程創建時線程執行堆棧的快照它還包含了給出有關錯誤更多信息的消息字符串最後它還可以包含 cause(原因)另一個導致此 throwable 拋出的 throwable它也稱為異常鏈 設施因為 cause 自身也會有 cause依此類推就形成了異常鏈每個異常都是由另一個異常引起的

  通俗的說異常鏈就是把原始的異常包裝為新的異常類並在新的異常類中封裝了原始異常類這樣做的目的在於找到異常的根本原因

  通過Throwable的兩個構造方法可以創建自定義的包含異常原因的異常類型Throwable(String message Throwable cause)

  構造一個帶指定詳細消息和 cause 的新 throwable Throwable(Throwable cause)

  構造一個帶指定 cause 和 (cause==null ? null causetoString())(它通常包含類和 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 causetoString()) 的詳細消息構造新異常(它通常包含 cause 的類和詳細消息)

  因此可以通過擴展Exception類來構造帶有異常原因的新的異常類

  七Java異常處理的原則和技巧

  避免過大的try塊不要把不會出現異常的代碼放到try塊裡面盡量保持一個try塊對應一個或多個異常

  細化異常的類型不要不管什麼類型的異常都寫成Excetpion catch塊盡量保持一個塊捕獲一類異常不要忽略捕獲的異常捕獲到後要麼處理要麼轉譯要麼重新拋出新類型的異常

  不要把自己能處理的異常拋給別人

  不要用try……catch參與控制程序流程異常控制的根本目的是處理程序的非正常情況


From:http://tw.wingwit.com/Article/program/Java/hx/201311/26045.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.