Java異常的深刻研究與分析

  前言java

  本文是異常內容的集大成者,力求全面,深刻的異常知識研究與分析。本文由金絲燕網獨家撰寫,參考衆多網上資源,通過內容辨別取捨,文字格式校驗等步驟編輯而成,以饗讀者。對於本文的內容,建議小白鬚要多多思考力求掌握,對於老手只需意會溫故知新。對於本文的內容,屬於基礎知識研究範疇,切勿覺得讀完此文就能將異常知識掌握到家。切記:操千曲然後曉聲,觀千劍然後識器,因此我以爲沒有大量的源碼閱讀經驗,你很難知道何時須要自定義異常,何時須要拋出異常。程序員

  異常機制概述數據庫

  異常機制是指當程序出現錯誤後,程序如何處理。具體來講,異常機制提供了程序退出的安全通道。當出現錯誤後,程序執行的流程發生改變,程序的控制權轉移到異常處理器。編程

  異常處理的流程安全

  當程序中拋出一個異常後,程序從程序中致使異常的代碼處跳出,java虛擬機檢測尋找和try關鍵字匹配的處理該異常的catch塊,若是找到,將控制權交到catch塊中的代碼,而後繼續往下執行程序,try塊中發生異常的代碼不會被從新執行。若是沒有找處處理該異常的catch塊,在全部的finally塊代碼被執行和當前線程的所屬的ThreadGroup的uncaughtException方法被調用後,遇到異常的當前線程被停止。函數

  異常的結構this

  異常的繼承結構:Throwable爲基類,Error和Exception繼承Throwable,RuntimeException和IOException等繼承Exception。Error和RuntimeException及其子類成爲未檢查異常(unchecked),其它異常成爲已檢查異常(checked)。線程

  

Java異常的深刻研究與分析(1)


  Error異常設計

  Error表示程序在運行期間出現了十分嚴重、不可恢復的錯誤,在這種狀況下應用程序只能停止運行,例如JAVA 虛擬機出現錯誤。Error是一種unchecked Exception,編譯器不會檢查Error是否被處理,在程序中不用捕獲Error類型的異常。通常狀況下,在程序中也不該該拋出Error類型的異常。cdn

  RuntimeException異常

  Exception異常包括RuntimeException異常和其餘非RuntimeException的異常。RuntimeException 是一種Unchecked Exception,即表示編譯器不會檢查程序是否對RuntimeException做了處理,在程序中沒必要捕獲RuntimException類型的異常,也沒必要在方法體聲明拋出RuntimeException類。RuntimeException發生的時候,表示程序中出現了編程錯誤,因此應該找出錯誤修改程序,而不是去捕獲RuntimeException。

  Checked Exception異常

  Checked Exception異常,這也是在編程中使用最多的Exception,全部繼承自Exception而且不是RuntimeException的異常都是checked Exception,上圖中的IOException和ClassNotFoundException。JAVA 語言規定必須對checked Exception做處理,編譯器會對此做檢查,要麼在方法體中聲明拋出checked Exception,要麼使用catch語句捕獲checked Exception進行處理,否則不能經過編譯。

  在聲明方法時候拋出異常

  語法:throws(略)

  爲何要在聲明方法拋出異常?

  方法是否拋出異常與方法返回值的類型同樣重要。假設方法拋出異常卻沒有聲明該方法將拋出異常,那麼客戶程序員能夠調用這個方法並且不用編寫處理異常的代碼。那麼,一旦出現異常,那麼這個異常就沒有合適的異常控制器來解決。

  爲何拋出的異常必定是已檢查異常?RuntimeException與Error能夠在任何代碼中產生,它們不須要由程序員顯示的拋出,一旦出現錯誤,那麼相應的異常會被自動拋出。遇到Error,程序員通常是無能爲力的;遇到RuntimeException,那麼必定是程序存在邏輯錯誤,要對程序進行修改;只有已檢查異常纔是程序員所關心的,程序應該且僅應該拋出或處理已檢查異常。而已檢查異常是由程序員拋出的,這分爲兩種狀況:客戶程序員調用會拋出異常的庫函數;客戶程序員本身使用throw語句拋出異常。

  注意:覆蓋父類某方法的子類方法不能拋出比父類方法更多的異常,因此,有時設計父類的方法時會聲明拋出異常,但實際的實現方法的代碼卻並不拋出異常,這樣作的目的就是爲了方便子類方法覆蓋父類方法時能夠拋出異常。

  在方法中如何拋出異常

  語法:throw(略)拋出什麼異常?

  對於一個異常對象,真正有用的信息是異常的對象類型,而異常對象自己毫無心義。好比一個異常對象的類型是ClassCastException,那麼這個類名就是惟一有用的信息。因此,在選擇拋出什麼異常時,最關鍵的就是選擇異常的類名可以明確說明異常狀況的類。

  異常對象一般有兩種構造函數:一種是無參數的構造函數;另外一種是帶一個字符串的構造函數,這個字符串將做爲這個異常對象除了類型名之外的額外說明。

  爲何要建立本身的異常?

  當Java內置的異常都不能明確的說明異常狀況的時候,須要建立本身的異常。須要注意的是,惟一有用的就是類型名這個信息,因此不要在異常類的設計上花費精力。

  throw和throws的區別

  public class TestThrow{ public static void main(String[] args)

  { try

  { //調用帶throws聲明的方法,必須顯式捕獲該異常

  //不然,必須在main方法中再次聲明拋出

  throwChecked(-3);

  } catch (Exception e)

  {

  System.out.println(e.getMessage());

  } //調用拋出Runtime異常的方法既能夠顯式捕獲該異常,

  //也可不理會該異常

  throwRuntime(3);

  } public static void throwChecked(int a)throws Exception { if (a 0)

  { //自行拋出Exception異常

  //該代碼必須處於try塊裏,或處於帶throws聲明的方法中

  throw new Exception(a的值大於0,不符合要求);

  }

  } public static void throwRuntime(int a)

  { if (a 0)

  { //自行拋出RuntimeException異常,既能夠顯式捕獲該異常

  //也可徹底不理會該異常,把該異常交給該方法調用者處理

  throw new RuntimeException(a的值大於0,不符合要求);

  }

  }

  }

  補充:throwChecked函數的另一種寫法以下所示:

  public static void throwChecked(int a)

  { if (a 0)

  { //自行拋出Exception異常

  //該代碼必須處於try塊裏,或處於帶throws聲明的方法中

  try

  { throw new Exception(a的值大於0,不符合要求);

  } catch (Exception e)

  { // TODO Auto-generated catch block

  e.printStackTrace();

  }

  }

  }

  注意:此時在main函數裏面throwChecked就不用try異常了。

  應該在聲明方法拋出異常仍是在方法中捕獲異常?

  處理原則:捕捉並處理哪些知道如何處理的異常,而傳遞哪些不知道如何處理的異常

  使用finally塊釋放資源

  finally關鍵字保證不管程序使用任何方式離開try塊,finally中的語句都會被執行。在如下三種狀況下會進入finally塊:

  (1) try塊中的代碼正常執行完畢。

  (2) 在try塊中拋出異常。

  (3) 在try塊中執行return、break、continue。

  所以,當你須要一個地方來執行在任何狀況下都必須執行的代碼時,就能夠將這些代碼放入finally塊中。當你的程序中使用了外界資源,如數據庫鏈接,文件等,必須將釋放這些資源的代碼寫入finally塊中。

  必須注意的是:在finally塊中不能拋出異常。JAVA異常處理機制保證不管在任何狀況下必須先執行finally塊而後再離開try塊,所以在try塊中發生異常的時候,JAVA虛擬機先轉到finally塊執行finally塊中的代碼,finally塊執行完畢後,再向外拋出異常。若是在finally塊中拋出異常,try塊捕捉的異常就不能拋出,外部捕捉到的異常就是finally塊中的異常信息,而try塊中發生的真正的異常堆棧信息則丟失了。請看下面的代碼:

  Connection con = null;try{

  con = dataSource.getConnection();

  ……

  }catch(SQLException e)

  {

  …… throw e;//進行一些處理後再將數據庫異常拋出給調用者處理}finally{ try

  {

  con.close();

  } catch(SQLException e)

  {

  e.printStackTrace();

  ……

  }

  }

  運行程序後,調用者獲得的信息以下

  java.lang.NullPointerException

  at myPackage.MyClass.method1(methodl.java:266)

  而不是咱們指望獲得的數據庫異常。這是由於這裏的con是null的關係,在finally語句中拋出了NullPointerException,在finally塊中增長對con是否爲null的判斷能夠避免產生這種狀況。

  丟失的異常

  請看下面的代碼:

  public void method2(){try{

  ……

  method1(); //method1進行了數據庫操做}catch(SQLException e)

  {

  …… throw new MyException(發生了數據庫異常:+e.getMessage);

  }

  }public void method3(){ try{

  method2();

  }catch(MyException e)

  {

  e.printStackTrace();

  ……

  }

  }

  上面method2的代碼中,try塊捕獲method1拋出的數據庫異常SQLException後,拋出了新的自定義異常MyException。這段代碼是否並無什麼問題,但看一下控制檯的輸出:

  MyException:發生了數據庫異常:對象名稱'MyTable' 無效。

  at MyClass.method2(MyClass.java:232)

  at MyClass.method3(MyClass.java:255)

  原始異常SQLException的信息丟失了,這裏只能看到method2裏面定義的MyException的堆棧狀況;而method1中發生的數據庫異常的堆棧則看不到,如何排錯呢,只有在method1的代碼行中一行行去尋找數據庫操做語句了。

  JDK的開發者們也意識到了這個狀況,在JDK1.4.1中,Throwable類增長了兩個構造方法,public Throwable(Throwable cause)和public Throwable(String message,Throwable cause),在構造函數中傳入的原始異常堆棧信息將會在printStackTrace方法中打印出來。但對於還在使用JDK1.3的程序員,就只能本身實現打印原始異常堆棧信息的功能了。實現過程也很簡單,只須要在自定義的異常類中增長一個原始異常字段,在構造函數中傳入原始異常,而後重載printStackTrace方法,首先調用類中保存的原始異常的printStackTrace方法,而後再調用super.printStackTrace方法就能夠打印出原始異常信息了。能夠這樣定義前面代碼中出現的MyException類:

  import java.io.PrintStream;import java.io.PrintWriter;public class MyException extends Exception{ private static final long serialVersionUID = 1L; //原始異常

  private Throwable cause; //構造函數

  public MyException(Throwable cause)

  { this.cause = cause;

  } public MyException(String s,Throwable cause)

  { super(s); this.cause = cause;

  } //重載printStackTrace方法,打印出原始異常堆棧信息

  public void printStackTrace()

  { if (cause != null)

  {

  cause.printStackTrace();

  } super.printStackTrace();

  } public void printStackTrace(PrintStream s)

  { if (cause != null)

  {

  cause.printStackTrace(s);

  } super.printStackTrace(s);

  } public void printStackTrace(PrintWriter s)

  { if (cause != null)

  {

  cause.printStackTrace(s);

  } super.printStackTrace(s);

  }

  }

相關文章
相關標籤/搜索