1. 程式人生 > >異常的概念和Java異常體系結構

異常的概念和Java異常體系結構

一、 異常的概念和Java異常體系結構 

    異常是程式執行過程中出現的錯誤。本文主要講授的是Java語言的異常處理。Java語言的異常處理框架, 
    是Java語言健壯性的一個重要體現。 

Java把異常當作物件來處理,並定義一個基類java.lang.Throwable作為所有異常的超類。 
在Java API中已經定義了許多異常類,這些異常類分為兩大類,錯誤Error和異常Exception。 
    Java異常體系結構呈樹狀,其層次結構圖如圖 1所示: 
    
     

    圖 1  Java異常體系結構 

    Thorwable類所有異常和錯誤的超類,有兩個子類Error和Exception,分別表示錯誤和異常。 
    其中異常類Exception又分為執行時異常(RuntimeException)和非執行時異常, 
    這兩種異常有很大的區別,也稱之為不檢查異常(Unchecked Exception) 
    和檢查異常(Checked Exception)。下面將詳細講述這些異常之間的區別與聯絡: 

    1、Error與Exception 

    Error是程式無法處理的錯誤,比如OutOfMemoryError、ThreadDeath等。這些異常發生時, 
    Java虛擬機器(JVM)一般會選擇執行緒終止。 


    Exception是程式本身可以處理的異常,這種異常分兩大類執行時異常和非執行時異常。 
    程式中應當儘可能去處理這些異常。 

    2、執行時異常和非執行時異常 

    執行時異常都是RuntimeException類及其子類異常,如NullPointerException、IndexOutOfBoundsException等, 
    這些異常是不檢查異常,程式中可以選擇捕獲處理,也可以不處理。這些異常一般是由程式邏輯錯誤引起的, 
    程式應該從邏輯角度儘可能避免這類異常的發生。 

    非執行時異常是RuntimeException以外的異常,型別上都屬於Exception類及其子類。 
    從程式語法角度講是必須進行處理的異常,如果不處理,程式就不能編譯通過。 
    如IOException、SQLException等以及使用者自定義的Exception異常,一般情況下不自定義檢查異常。 


二、 異常的捕獲和處理 



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


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

    1、 異常處理的基本語法 
    在java中,異常處理的完整語法是:

Java程式碼 
  1. try{  
  2. //(嘗試執行的)程式程式碼 
  3. }catch(異常型別 異常的變數名){  
  4. //異常處理程式碼 
  5. }finally{  
  6. //異常發生,方法返回之前,總是要執行的程式碼 
  1. try
  2.   //(嘗試執行的)程式程式碼
  3. }catch(異常型別 異常的變數名){ 
  4.   //異常處理程式碼
  5. }finally
  6.   //異常發生,方法返回之前,總是要執行的程式碼
  1. <span style="color:#000000;">     try{  
  2.       //(嘗試執行的)程式程式碼
  3.     }catch(異常型別 異常的變數名){  
  4.       //異常處理程式碼
  5.     }finally{  
  6.       //異常發生,方法返回之前,總是要執行的程式碼
  7.     }  
  8. </span>  





    以上語法有三個程式碼塊: 
    try語句塊,表示要嘗試執行程式碼,try語句塊中程式碼受異常監控,其中程式碼發生異常時,會丟擲異常物件。 

    catch語句塊會捕獲try程式碼塊中發生的異常並在其程式碼塊中做異常處理,catch語句帶一個Throwable型別的引數, 
    表示可捕獲異常型別。當try中出現異常時,catch會捕獲到發生的異常,並和自己的異常型別匹配, 
    若匹配,則執行catch塊中程式碼,並將catch塊引數指向所拋的異常物件。catch語句可以有多個, 
    用來匹配多箇中的一個異常,一旦匹配上後,就不再嘗試匹配別的catch塊了。 
    通過異常物件可以獲取異常發生時完整的JVM堆疊資訊,以及異常資訊和異常發生的原因等。 

    finally語句塊是緊跟catch語句後的語句塊,這個語句塊總是會在方法返回前執行, 
    而不管是否try語句塊是否發生異常。並且這個語句塊總是在方法返回前執行。 
    目的是給程式一個補救的機會。這樣做也體現了Java語言的健壯性。 

    2、 try、catch、finally三個語句塊應注意的問題 
    第一、try、catch、finally三個語句塊均不能單獨使用,三者可以組成 try...catch...finally、try...catch、 
    try...finally三種結構,catch語句可以有一個或多個,finally語句最多一個。 
    第二、try、catch、finally三個程式碼塊中變數的作用域為程式碼塊內部,分別獨立而不能相互訪問。 
    如果要在三個塊中都可以訪問,則需要將變數定義到這些塊的外面。 
    第三、多個catch塊時候,只會匹配其中一個異常類並執行catch塊程式碼,而不會再執行別的catch塊, 
    並且匹配catch語句的順序是由上到下。 

    3、throw、throws關鍵字 
    throw關鍵字是用於方法體內部,用來丟擲一個Throwable型別的異常。如果丟擲了檢查異常, 
    則還應該在方法頭部宣告方法可能丟擲的異常型別。該方法的呼叫者也必須檢查處理丟擲的異常。 
    如果所有方法都層層上拋獲取的異常,最終JVM會進行處理,處理也很簡單,就是列印異常訊息和堆疊資訊。 
    如果丟擲的是Error或RuntimeException,則該方法的呼叫者可選擇處理該異常。有關異常的轉譯會在下面說明。 

    throws關鍵字用於方法體外部的方法宣告部分,用來宣告方法可能會丟擲某些異常。僅當丟擲了檢查異常, 
    該方法的呼叫者才必須處理或者重新丟擲該異常。當方法的呼叫者無力處理該異常的時候,應該繼續丟擲, 
    而不是囫圇吞棗一般在catch塊中列印一下堆疊資訊做個勉強處理。下面給出一個簡單例子, 
    看看如何使用這兩個關鍵字:

Java程式碼 
  1. publicstaticvoid test3() throws Exception{  
  2. //丟擲一個檢查異常 
  3. thrownew Exception("方法test3中的Exception");  
  4.     }  
  1. publicstaticvoid test3() throws Exception{ 
  2.   //丟擲一個檢查異常
  3.         thrownew Exception("方法test3中的Exception"); 
  4.     }  
  1. <span style="color:#000000;">    publicstaticvoid test3() throws Exception{  
  2.       //丟擲一個檢查異常
  3.             thrownew Exception("方法test3中的Exception");  
  4.         }   
  5. </span>  



    4、 Throwable類中的常用方法 
    getCause():返回丟擲異常的原因。如果 cause 不存在或未知,則返回 null。 
    getMessage():返回異常的訊息資訊。 
    printStackTrace():物件的堆疊跟蹤輸出至錯誤輸出流,作為欄位 System.err 的值。 



三、 異常處理的一般原則 

    1、 能處理就早處理,丟擲不去還不能處理的就想法消化掉或者轉換為RuntimeException處理。 
    因為對於一個應用系統來說,丟擲大量異常是有問題的,應該從程式開發角度儘可能的控制異常發生的可能。 
    2、 對於檢查異常,如果不能行之有效的處理,還不如轉換為RuntimeException丟擲。 
    這樣也讓上層的程式碼有選擇的餘地――可處理也可不處理。 
    3、 對於一個應用系統來說,應該有自己的一套異常處理框架,這樣當異常發生時,也能得到統一的處理風格, 
    將優雅的異常資訊反饋給使用者。 

四、 異常的轉譯與異常鏈 

    1、異常轉譯的原理 


    所謂的異常轉譯就是將一種異常轉換另一種新的異常,也許這種新的異常更能準確表達程式發生異常。 
    在Java中有個概念就是異常原因,異常原因導致當前丟擲異常的那個異常物件, 
    幾乎所有帶異常原因的異常構造方法都使用Throwable型別做引數,這也就為異常的轉譯提供了直接的支援, 
    因為任何形式的異常和錯誤都是Throwable的子類。比如將SQLException轉換為另外一個新的異常DAOException, 
    可以這麼寫: 

    先自定義一個異常DAOException:

Java程式碼 
  1. publicclass DAOException extends RuntimeException {  
  2. /(省略了部分程式碼)  
  3. public DAOException(String message, Throwable cause) {  
  4. super(message, cause);  
  5.   }  
  1. publicclass DAOException extends RuntimeException { 
  2. /(省略了部分程式碼) 
  3.   public DAOException(String message, Throwable cause) { 
  4.       super(message, cause); 
  5.   } 
  1. <span style="color:#000000;">      publicclass DAOException extends RuntimeException {  
  2.      //(省略了部分程式碼)
  3.         public DAOException(String message, Throwable cause) {  
  4.             super(message, cause);  
  5.         }  
  6.     }  
  7. </span>  



    比如有一個SQLException型別的異常物件e,要轉換為DAOException,可以這麼寫:

Java程式碼 
  1. DAOException daoEx = new DAOException ( "SQL異常", e);  
  1. DAOException daoEx = new DAOException ( "SQL異常", e);  
  1. <span style="color:#000000;">    DAOException daoEx = new DAOException ( "SQL異常", e);   
  2. </span>  



    異常轉譯是針對所有繼承Throwable超類的類而言的,從程式設計的語法角度講,其子類之間都可以相互轉換。 
    但是,從合理性和系統設計角度考慮,可將異常分為三類:Error、Exception、RuntimeException,筆者認為, 
    合理的轉譯關係圖應該如圖 2: 




    圖 2 異常轉譯 

    為什麼要這麼做呢?筆者認為,異常的處理存在著一套哲學思想:對於一個應用系統來說, 
    系統所發生的任何異常或者錯誤對操作使用者來說都是系統"執行時"異常,都是這個應用系統內部的異常。 
    這也是異常轉譯和應用系統異常框架設計的指導原則。在系統中大量處理非檢查異常的負面影響很多, 
    最重要的一個方面就是程式碼可讀性降低,程式編寫複雜,異常處理的程式碼也很蒼白無力。 
    因此,很有必要將這些檢查異常Exception和錯誤Error轉換為RuntimeException異常, 
    讓程式設計師根據情況來決定是否捕獲和處理所發生的異常。 


    圖中的三條線標識轉換的方向,分三種情況: 

    ①:Error到Exception:將錯誤轉換為異常,並繼續丟擲。例如spring WEB框架中, 
    將org.springframework.web.servlet.DispatcherServlet的doDispatch()方法中, 
    將捕獲的錯誤轉譯為一個NestedServletException異常。這樣做的目的是為了最大限度挽回因錯誤發生帶來的負面影響。 
    因為一個Error常常是很嚴重的錯誤,可能會引起系統掛起。 

    ②:Exception到RuntimeException:將檢查異常轉換為RuntimeException可以讓程式程式碼變得更優雅, 
    讓開發人員集中經理設計更合理的程式程式碼,反過來也增加了系統發生異常的可能性。 

    ③:Error到RuntimeException:目的還是一樣的。把所有的異常和錯誤轉譯為不檢查異常, 
    這樣可以讓程式碼更為簡潔,還有利於對錯誤和異常資訊的統一處理。 


    1、 異常鏈 

    異常鏈顧名思義就是將異常發生的原因一個傳一個串起來,即把底層的異常資訊傳給上層,這樣逐層丟擲。 
    Java API文件中給出了一個簡單的模型:

Java程式碼 
  1. try {  
  2. lowLevelOp();  
  3. catch (LowLevelException le) {  
  4. throw (HighLevelException)  
  5. new HighLevelException().initCause(le);  
  1. try { 
  2. lowLevelOp(); 
  3. catch (LowLevelException le) { 
  4.   throw (HighLevelException) 
  5.   new HighLevelException().initCause(le); 
  1. <span style="color:#000000;">    try {  
  2.      lowLevelOp();  
  3.     } catch (LowLevelException le) {  
  4.       throw (HighLevelException)  
  5.       new HighLevelException().initCause(le);  
  6.     }  
  7. </span>  



    當程式捕獲到了一個底層異常le,在處理部分選擇了繼續丟擲一個更高級別的新異常給此方法的呼叫者。 
    這樣異常的原因就會逐層傳遞。這樣,位於高層的異常遞迴呼叫getCause()方法,就可以遍歷各層的異常原因。 
    這就是Java異常鏈的原理。異常鏈的實際應用很少,發生異常時候逐層上拋不是個好注意, 
    上層拿到這些異常又能奈之何?而且異常逐層上拋會消耗大量資源, 
    因為要儲存一個完整的異常鏈資訊. 


五、 設計一個高效合理的異常處理框架 

    對於一個應用系統來說,發生所有異常在使用者看來都是應用系統內部的異常。因此應該設計一套應用系統的異常框架, 
    以處理系統執行過程中的所有異常。 

    基於這種觀點,可以設計一個應用系統的異常比如叫做AppException。並且對使用者來說, 
    這些異常都是執行應用系統執行時發生的,因此AppException應該繼承RuntimeException, 
    這樣系統中所有的其他異常都轉譯為AppException,當異常發生的時候,前端接收到AppExcetpion並做統一的處理。 
    
    畫出異常處理框架如圖 3 : 





     圖 3 一個應用系統的異常處理框架 

    在這個設計圖中,AppRuntimeException是系統異常的基類,對外只丟擲這個異常, 
    這個異常可以由前端(客戶端)接收處理,當異常發生時,客戶端的相關元件捕獲並處理這些異常, 
    將"友好"的資訊展示給客戶。 

    在AppRuntimeException下層,有各種各樣的異常和錯誤,最終都轉譯為AppRuntimeException, 
    AppRuntimeException下面還可以設計一些別的子類異常,比如AppDAOException、OtherException等, 
    這些都根據實際需要靈活處理。 
    在往下就是如何將捕獲的原始異常比如SQLException、HibernateException轉換為更高階一點AppDAOException。 


    有關異常框架設計這方面公認比較好的就是Spring,Spring中的所有異常都可以用org.springframework.core.NestedRuntimeException來表示,並且該基類繼承的是RuntimeException。 
    Spring框架很龐大,因此設計了很多NestedRuntimeException的子類,還有異常轉換的工具, 
    這些都是非常優秀的設計思想。 


六、 Java異常處理總結 

    回顧全文,總結一下Java異常處理的要點: 
    1、 異常是程式執行過程過程出現的錯誤,在Java中用類來描述,用物件來表示具體的異常。 
        Java將其區分為Error與Exception,Error是程式無力處理的錯誤,Exception是程式可以處理的錯誤。 
        異常處理是為了程式的健壯性。 
    2、 Java異常類來自於Java API定義和使用者擴充套件。通過繼承Java API異常類可以實現異常的轉譯。 
    3、 異常能處理就處理,不能處理就丟擲,最終沒有處理的異常JVM會進行處理。 
    4、 異常可以傳播,也可以相互轉譯,但應該根據需要選擇合理的異常轉譯的方向。 
    5、 對於一個應用系統,設計一套良好的異常處理體系很重要。這一點在系統設計的時候就應該考慮到。

相關推薦

異常概念Java異常體系結構

一、 異常的概念和Java異常體系結構      異常是程式執行過程中出現的錯誤。本文主要講授的是Java語言的異常處理。Java語言的異常處理框架,      是Java語言健壯性的一個重要體現。  Java把異常當作物件來處理,並定義一個基類java.lang.Thr

【計算機網路基礎概念】2網路體系結構協議與區域網基礎概念

目錄 一、網路體系結構及協議 1、ISO/OSI參考模型 2、網路協議 3、IP地址與子網掩碼 二、區域網 1、區域網 2、虛擬區域網 一、網路體系結構及協議 1、ISO/OSI參考模型 在OSI參考模型中,將整個通訊功能分為7層:物理層、資料鏈路層、

C++java異常處理中關於finally的區別

在java中,異常處理由try{}catch(){}finally{}組成,無論try中有沒有異常,try和catch中有沒有return,finally最終都會執行。而且finally中若有return,則此return和try、catch中的return相比

Python Java 異常處理對比

當你在編寫程式時,可能由於人為或其它因素導致發生異常後,如果不對異常處理,程式就無法繼續執行。Python語言中也有自己的異常處理,下面我們先看下Java中的異常處理,然後再對比Python中的異常處理,其實它們都大同小異。java 中的異常處理我們不做過多介紹

有關使用部署 Java 永續性體系結構 (JPA) 的案例研究

2006 年夏天釋出的 EJB 3.0 規範提供了一個大大簡化但功能更為強大的 EJB 框架,該框架演示了批註與傳統 EJB 2.x 部署描述符相比的顯著優勢。J2SE 5.0 中引入的批註是修飾符,可以在類、欄位、方法、引數、本地變數、構造符、列舉和程式包中使用。大量 EJ

Android 的一些基本概念OOM異常的處理方法

一些基本的概念 ActivityManagerServices,簡稱AMS,服務端物件,負責系統中所有的Activity的生命週期 ActivityThread,App的真正入口。當開啟App之後,會呼叫main()開始執行,開啟訊息迴圈佇列,這就是傳說中的

Laravel 5.1 中的異常處理器HTTP異常處理 abort()

錯誤日誌 exce ant upload 記錄 再次 .org splay don 原文 http://laravelacademy.org/post/1867.html 錯誤和異常是處理程序開發中不可回避的議題,在本地開發中我們往往希望能捕獲程序拋出的異常並將其顯示打印

Java加密體系結構(JCA)參考指南

介紹 Java平臺強調安全性,包括語言安全,密碼學,公鑰基礎設施,認證,安全通訊和訪問控制。 JCA是平臺的一個主要部分,包含一個“Provider”體系結構和一組用於數字簽名,訊息摘要(雜湊),證書和證書驗證,加密(對稱/非對稱塊/流密碼),金鑰生成 管理和

馮諾依曼體系結構哈弗體系結構-計算機兩大體系結構

計算機兩大體系結構: 1.馮諾依曼體系結構 計算機系統由一箇中央處理單元CPU和一個儲存器組成。 儲存器擁儲存資料和指令,並且可以根據所給的地址對它進行讀或寫。 資料和指令都儲存在儲存器中的計算機被稱為馮諾依曼體系結構。 2.哈弗曼體系結構 資料和程式指

Python Flask Web 第一課 —— 基本概念程式的基本結構

1. 初始化 所有的 Flask 程式都必須建立一個程式例項,所謂程式例項,在 Flask 框架下就是,Flask 類的例項物件(instance)。 from flask import Flask app = Flask(__name__) Web

[AnyDAC][Phys][ODBC][Microsoft][ODBC 驅動管理器]在指定的DSN中,驅動程式應用程式體系結構

使用plsql的odbc匯入器的時候會出現該問題: 原因是我是64位的系統!!! 解決方法:1.執行C:\Windows\SysWOW64\odbcad32.exe,開啟後如下圖所示: 2.點選新增,選擇如下圖所示Microsoft Excel Driver(*.xls)

淺談OSI/RM體系結構TCP/IP體系結構

計算機網路的各層及其協議的集合稱為網路的體系結構。 提出的目的是為讓全世界異構的網路互聯起來,更好的相互交換資料,必須制定一些規則,即協議, 協議有三要素: 語法:資料與控制資訊的結構或格式 語義:需要發出何種控制資訊,完成何種動作以及做出何種響應 同步:事件實現順序說明(

JVM 記憶體分配模型概念java中各種物件的儲存

Java 方法棧也是執行緒私有的,每個 Java 方法棧都是由一個個棧幀組成的,每個棧幀是一個方法執行期的基礎資料結構,它儲存著區域性變量表、運算元棧、動態連結、方法出口等資訊。當執行緒呼叫呼叫了一個 Java 方法時,一個棧幀就被壓入(push)到相應的 Java 方法棧。當執行緒從一個 Java 方法

Laravel 中的異常處理器HTTP異常處理例項教程

Laravel應用中所有的異常都通過 App\Exceptions\Handler 進行處理,下面我們先簡單分析下給異常處理器類的屬性和方法: $dontReport屬性 protected $d

Synchronized加鎖、鎖升級java物件記憶體結構

首先了解一下JMM中定義的記憶體操作: 一個執行緒操作資料時候都是從主記憶體(堆記憶體)讀取到自己工作記憶體(執行緒私有的資料區域)中再進行操作。對於硬體記憶體來說,並沒有工作記憶體和主記憶體的區分,這都是java記憶體模型劃分出來的,它只是一種抽象的概念,是一組規則,並不是實際存在的。Java記憶體模型中定

java異常異常體系

urn 執行過程 必須 sys ... 值類型 存在 6.2 結果 16.異常 16.1程序執行過程中出現的影響程序正常運行的現象。 16.2異常語法 try{ //代碼塊 }catch(異常類型 e){ }catch(異常類型2 e2){

java之程式的異常體系結構,實習面試點

java之程式的異常體系結構 1. 程式中的異常 2. java異常層次體系結構圖 3. java異常的體系結構,實習面試點 java異常程式碼體會 1. 程式中的異常 不可避免的異常,在系統的

JAVA高階--異常處理概念異常處理機制

什麼是異常   程式執行的過程中發生的一些不正常事件 異常分類     Throwable       Error  錯誤         Exception           IOException            

Java異常體系結構 侵立刪

轉自:http://www.importnew.com/18994.html 在程式設計中,進行異常處理是非常關鍵和重要的一部分。一個程式的異常處理框架的好壞直接影響到整個專案的程式碼質量以及後期維護成本和難度。試想一下,如果一個專案從頭到尾沒有考慮過異常處理,當程式出錯從哪裡尋找出錯的根源?但

java異常體系以及異常的捕獲處理圖解詳解

java 異常是程式執行過程中出現的錯誤。Java把異常當作物件來處理,並定義一個基類java.lang.Throwable作為所有異常的超類。在Java API中定義了許多異常類,分為兩大類,錯誤Error和異常Exception。其中異常類Exception又分為執行