天天看點

深入探讨Java中的異常與錯誤處理

深入探讨Java中的異常與錯誤處理

java中的異常處理機制已經比較成熟,我們的java程式到處充滿了異常的可能,如果對這些異常不做預先的處理,那麼将來程式崩潰就無從調試,很難找到異常所在的位置。本文将探讨一下java中異常與錯誤的處理方法,一起來看看。

異常與錯誤:

異常:

在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() { 

     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 { 

          bufferedinputstream in = new bufferedinputstream(new fileinputstream(file)); 

     } catch (filenotfoundexception e) { 

          e.printstacktrace(); 

          system.err.println("不知道如何處理該異常或者根本不想處理它,但是不做處理又不合适,這是重新抛出異常交給上一級處理"); 

          //重新抛出異常 

          throw e; 

public static void printfile(string file) { 

          readfile(file); 

     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) { 

異常丢失的情況:

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; 

          } 

               g(); 

          } catch (exceptionc e) { 

               e.printstacktrace(); 

/* 

exception.exceptionc 

at exception.nevercaught.g(nevercaught.java:12) 

at exception.nevercaught.main(nevercaught.java:19) 

*/  

為什麼隻是列印出來了exceptionc而沒有列印出exceptionb呢?這個還是自己分析一下吧!

上面的情況相當于少了一種異常,這在我們排錯的過程中非常的不利。那我們遇到上面的情況應該怎麼辦呢?這就是異常鍊的用武之地:儲存異常資訊,在抛出另外一個異常的同時不丢失原來的異常。

           throw new exceptionb("exception b"); 

                f(); 

                exceptionc c = new exceptionc("exception a"); 

                //異常連 

                c.initcause(e); 

                throw c; 

                g(); 

                e.printstacktrace(); 

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; 

           reader = new bufferedreader(new inputstreamreader(new fileinputstream(file))); 

           // do some other work 

           e.printstacktrace(); 

     } finally { 

           try { 

                 reader.close(); 

           } catch (ioexception e) { 

                 e.printstacktrace(); 

           } 

例子非常的簡單,是一個讀取檔案的例子。這樣的例子在jdbc操作中也非常的常見。(是以,我覺得對于資源的及時正确清理是一個程式員的基本素質之一。)

try…finally結構也是保證資源正确關閉的一個手段。如果你不清楚代碼執行過程中會發生什麼異常情況會導緻資源不能得到清理,那麼你就用try對這段”可疑”代碼進行包裝,然後在finally中進行資源的清理。舉一個例子:

public void readfile() { 

           reader = new bufferedreader(new inputstreamreader(new fileinputstream("file"))); 

           //close reader 

           reader.close(); 

     } catch (ioexception e) { 

我們注意一下這個方法和上一個方法的差別,下一個人可能習慣更好一點,及早的關閉reader。但是往往事與願違,因為在reader.close()以前異常随時可能發生,這樣的代碼結構不能預防任何異常的出現。因為程式會在異常出現的地方跳出,後面的代碼不能執行(這在上面應經用執行個體證明過)。這時我們就可以用try…finally來改造:

                 reader = new bufferedreader(new inputstreamreader(new fileinputstream("file"))); 

                 // do some other work 

                 // close reader 

           } finally { 

      } catch (filenotfoundexception e) { 

      } catch (ioexception e) { 

      } 

及早的關閉資源是一種良好的行為,因為時間越長你忘記關閉的可能性越大。這樣在配合上try…finally就保證萬無一失了(不要嫌麻煩,java就是這麼中規中矩)。

再說一種情況,假如我想在構造方法中打開一個檔案或者建立一個jdbc連接配接,因為我們要在其他的方法中使用這個資源,是以不能在構造方法中及早的将這個資源關閉。那我們是不是就沒轍了呢?答案是否定的。看一下下面的例子:

public class resourceinconstructor { 

     public resourceinconstructor() { 

                reader = new bufferedreader(new inputstreamreader(new fileinputstream(""))); 

          } catch (filenotfoundexception e) { 

     public void readfile() { 

                 while(reader.readline()!=null) { 

                      //do some work 

                 } 

          } catch (ioexception e) { 

      public void dispose() { 

                reader.close(); 

這一部分講的多了一點,但是異常确實是看起來容易用起來難的東西呀,java中還是有好多的東西需要深挖的。

作者:佚名

來源:51cto