異常與錯誤:
異常: 在Java中程式的錯誤主要是語法錯誤和語義錯誤,一個程式在編譯和運行時出現的錯誤我們統一稱之為異常,它是VM(虛擬機)通知你的一種方式,透過這種方式,VM讓你知道,你(開發人員)已經犯了個錯誤,現在有一個機會來修改它。 Java中使用異常類別來表示異常,不同的異常類別代表了不同的異常。但是在Java中所有的異常都有一個基底類,叫做Exception。
錯誤:它指的是一個合理的應用程式不能截獲的嚴重的問題。大多數都是反常的情況。錯誤是VM的一個故障(雖然它可以是任何系統級的服務)。所以,錯誤是很難處理的,一般的開發人員(當然不是你)是無法處理這些錯誤的,例如記憶體溢位。 和異常一樣,在Java中用錯誤類別來表示錯誤,不同的錯誤類別代表了不同的錯誤。 但是在Java中所有的錯誤都有一個基類,叫做Error。
綜上,我們可以知道異常和錯誤最本質的區別就是異常能被開發人員處理而錯誤時系統本來自帶的,一般無法處理也不需要我們程式設計師來處理。
1.一個異常是在一個程序執行過程中出現的一個事件,它中斷了正常指令的運行
2.錯誤,偏離了可接受的代碼行為的一個動作或實例
異常的結構分類:
1、執行時期異常(未檢查異常)
2、編譯時異常(已檢查異常)
運行異常即是RuntimeException;其餘的全部為編譯異常
在Java中異常Exception和錯誤Error有個共同的父類Throwable。
Error Exception
runtimeException幾個子類別
1、 java.lang.ArrayIndexOutOfBoundsException
陣列索引越界異常。當數組的索引值為負數或大於等於數組大小時拋出。
2、java.lang.ArithmeticException
算術條件異常。譬如:整數除零等。
3、java.lang.NullPointerException
空指標異常。當應用程式試圖在要求使用物件的地方使用了null時,拋出該異常。譬如:呼叫null物件的實例方法、存取null物件的
屬性、計算null物件的長度、使用throw語句拋出null等等
4、java.lang.ClassNotFoundException
找不到類別異常。當應用試圖根據字串形式的類別名稱構造類,而在遍歷CLASSPAH之後找不到對應名稱的class檔案時,拋出
該異常。
對異常的處理:
try{}catch{}
try{}catch{}finally{}無論有無異常finally程式碼區塊都會被執行
try{}finally{}也是可以組合使用的但是catch{}finally {}不可以
注意:在繼承關係中,子類覆蓋父類的方法,拋出異常的範圍不能比父類更寬泛
異常的使用
在異常的使用這一部分主要是演示代碼,都是我們平常寫程式碼的過程中會遇到的(當然只是一小部分),拋磚引玉嗎!
例1. 這個例子主要透過兩個方法對比來示範有了異常以後程式碼的執行流程。
public static void testException1() { int[] ints = new int[] { 1, 2, 3, 4 }; System.out.println("异常出现前"); try { System.out.println(ints[4]); System.out.println("我还有幸执行到吗");// 发生异常以后,后面的代码不能被执行 } catch (IndexOutOfBoundsException e) { System.out.println("数组越界错误"); } System.out.println("异常出现后"); } /*output: 异常出现前 数组越界错误 常出现后 */ 代码如下 复制代码 public static void testException2() { int[] ints = new int[] { 1, 2, 3, 4 }; System.out.println("异常出现前"); System.out.println(ints[4]); System.out.println("我还有幸执行到吗");// 发生异常以后,他后面的代码不能被执行 }
首先指出例子中的不足之處,IndexOutofBoundsException是一個非受檢異常,所以不用try...catch...顯示捕捉,但是我的目的是對同一個異常用不同的處理方式,看它會有什麼不同的而結果(這裡也就只能用它將就一下了)。異常出現時第一個方法只是跳出了try塊,但是它後面的程式碼會照樣執行的。但是第二種就不一樣了直接跳出了方法,比較強硬。從第一個方法我們看到,try...catch...是一種"事務性"的保障,它的目的是保證程式在異常的情況下運行完畢,同時它也會告知程式設計師程式中出錯的詳細資訊(這種詳細資訊有時要依賴程式設計師設計)。
例2. 重新拋出異常
public class Rethrow { public static void readFile(String file) throws FileNotFoundException { try { BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); } catch (FileNotFoundException e) { e.printStackTrace(); System.err.println("不知道如何处理该异常或者根本不想处理它,但是不做处理又不合适,这是重新抛出异常交给上一级处理"); //重新抛出异常 throw e; } } public static void printFile(String file) { try { readFile(file); } catch (FileNotFoundException e) { e.printStackTrace(); } } public static void main(String[] args) { printFile("D:/file"); } }
異常的本意是好的,讓我們試圖修復程序,但是現實中我們修復的幾率很小,我們很多時候就是用它來記錄出錯的信息。如果你厭倦了不停的處理異常,重新拋出異常對你來說可能是一個很好的解脫。原封不動的把這個異常拋給上一級,拋給調用這個方法的人,讓他來費腦筋吧。這樣看來,java異常(當然指的是受檢異常)又給我們平添很多麻煩,儘管它的出發點是好的。
例3. 異常鏈的使用及異常丟失
ExceptionA,ExceptionB,ExceptionC public class ExceptionA extends Exception { public ExceptionA(String str) { super(); } } public class ExceptionB extends ExceptionA { public ExceptionB(String str) { super(str); } } public class ExceptionC extends ExceptionA { public ExceptionC(String str) { super(str); } }
異常丟失的情況:
public class NeverCaught { static void f() throws ExceptionB{ throw new ExceptionB("exception b"); } static void g() throws ExceptionC { try { f(); } catch (ExceptionB e) { ExceptionC c = new ExceptionC("exception a"); throw c; } } public static void main(String[] args) { try { g(); } catch (ExceptionC e) { e.printStackTrace(); } } } /* exception.ExceptionC at exception.NeverCaught.g(NeverCaught.java:12) at exception.NeverCaught.main(NeverCaught.java:19) */
為什麼只是打印出來了ExceptionC而沒有打印出ExceptionB呢?這個還是自己分析一下吧!
上面的情況相當於少了一種異常,這在我們排錯的過程中非常的不利。那我們遇到上面的情況該怎麼辦呢?這就是異常鏈的用武之地:保存異常訊息,在拋出另外一個異常的同時不丟失原來的異常。
public class NeverCaught { static void f() throws ExceptionB{ throw new ExceptionB("exception b"); } static void g() throws ExceptionC { try { f(); } catch (ExceptionB e) { ExceptionC c = new ExceptionC("exception a"); //异常连 c.initCause(e); throw c; } } public static void main(String[] args) { try { g(); } catch (ExceptionC e) { e.printStackTrace(); } } } /* exception.ExceptionC at exception.NeverCaught.g(NeverCaught.java:12) at exception.NeverCaught.main(NeverCaught.java:21) Caused by: exception.ExceptionB at exception.NeverCaught.f(NeverCaught.java:5) at exception.NeverCaught.g(NeverCaught.java:10) ... 1 more */
这个异常链的特性是所有异常均具备的,因为这个initCause()方法是从Throwable继承的。
例4. 清理工作
清理工作对于我们来说是必不可少的,因为如果一些消耗资源的操作,比如IO,JDBC。如果我们用完以后没有及时正确的关闭,那后果会很严重,这意味着内存泄露。异常的出现要求我们必须设计一种机制不论什么情况下,资源都能及时正确的清理。这就是finally。
public void readFile(String file) { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader( new FileInputStream(file))); // do some other work } catch (FileNotFoundException e) { e.printStackTrace(); } finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } }
例子非常的简单,是一个读取文件的例子。这样的例子在JDBC操作中也非常的常见。(所以,我觉得对于资源的及时正确清理是一个程序员的基本素质之一。)
Try...finally结构也是保证资源正确关闭的一个手段。如果你不清楚代码执行过程中会发生什么异常情况会导致资源不能得到清理,那么你就用try对这段"可疑"代码进行包装,然后在finally中进行资源的清理。举一个例子:
public void readFile() { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader( new FileInputStream("file"))); // do some other work //close reader reader.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
我们注意一下这个方法和上一个方法的区别,下一个人可能习惯更好一点,及早的关闭reader。但是往往事与愿违,因为在reader.close()以前异常随时可能发生,这样的代码结构不能预防任何异常的出现。因为程序会在异常出现的地方跳出,后面的代码不能执行(这在上面应经用实例证明过)。这时我们就可以用try...finally来改造:
public void readFile() { BufferedReader reader = null; try { try { reader = new BufferedReader(new InputStreamReader( new FileInputStream("file"))); // do some other work // close reader } finally { reader.close(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
及早的关闭资源是一种良好的行为,因为时间越长你忘记关闭的可能性越大。这样在配合上try...finally就保证万无一失了(不要嫌麻烦,java就是这么中规中矩)。
再说一种情况,假如我想在构造方法中打开一个文件或者创建一个JDBC连接,因为我们要在其他的方法中使用这个资源,所以不能在构造方法中及早的将这个资源关闭。那我们是不是就没辙了呢?答案是否定的。看一下下面的例子:
public class ResourceInConstructor { BufferedReader reader = null; public ResourceInConstructor() { try { reader = new BufferedReader(new InputStreamReader(new FileInputStream(""))); } catch (FileNotFoundException e) { e.printStackTrace(); } } public void readFile() { try { while(reader.readLine()!=null) { //do some work } } catch (IOException e) { e.printStackTrace(); } } public void dispose() { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } }
这一部分讲的多了一点,但是异常确实是看起来容易用起来难的东西呀,java中还是有好多的东西需要深挖的
更多java异常与错误处理基本知识相关文章请关注PHP中文网!