當異常發生時,原本要接著執行的代碼不再執行,轉而讓其他部分的代碼來處理。如果沒有代碼負責處理,控制台會報告異常。
異常出現時的執行機制:
異常機制最大的好處是:清晰地分開了 正常的業務邏輯 和 遇到情況時的處理 代碼。(當在業務邏輯中,有多步可能會拋出不同的異常時,異常處理機制的好處更得以體現。如果沒有這種機制,也許會通過很多的if...else...來實現異常處理,甚至是多層嵌套的if...else...,這樣的代碼可讀性很差)
通過例子來理解:
package exception; public class ExceptionCatch { public static void main(String[] args) {
try {
//我們通過拋出異常來抽象真實的業務邏輯,可能某一步會出現異常,這時下面的代碼就不再執行,轉而到處理代碼。
//執行完處理代碼之後不會再回到之前try裡沒執行完的代碼繼續執行,而是去往下執行try{}catch{}之後的代碼。 throw new NullPointerException();
throw new ArrayIndexOutOfBoundsException();
throw ...
}catch(NullPointerException e) { //異常處理代碼 }catch(ArrayIndexOutOfBoundsException e) {
//異常處理代碼
}catch(Exception e){
//異常處理代碼
} } }
-----------------------------------------------------
捕捉到異常之後怎麼處理?
當catch到一場之後,根據你的業務邏輯來對它進行處理,比如說可以彈出一個窗口來警告用戶發生了錯誤,或者讓程序自己重新執行或者終止掉等等。當然,如果現在處理不了這個異常,也可以將它再次拋出。
處理完異常之後是回不到異常發生的地方繼續執行一下的代碼了,而是從catch{}後面的代碼開始執行。
一個異常只能被捕捉一次,捕捉之後這個異常就沒有了。不可能再次捕捉到。
-----------------------------------------------------
自定義異常類的構造器 與 getMessage()方法的使用 :
1 package exception; 2 3 //注意觀察兩個異常類構造器的區別。
/**
* 推斷:
* 第一個異常類是自己定義了一個String變量,在構造的時候是將信息傳給了這個變量;
* 而第二個異常類的構造器是覆蓋了父類的構造方法,所以我們猜測,父類Exception裡一定有一個String類型的成員變量(這個成員變量同樣繼承給了MyException子類),
* 所以在構造的時候利用super()將父類的構造方法取過來,進而將信息傳給了父類裡的那個String成員變量。
* 而getMessage()方法也是從父類那裡繼承來的,進而我們推斷:getMessage()方法返回的正是那個String變量。
* 在第一個異常類的那個從父類繼承的String變量並沒有被賦值,所以通過getMessage()取到的是null。
*/
4 class MyException extends Exception { 5 String msg; 6 public MyException(String msg) { 7 this.msg = msg; 8 } 9 public void printMsg() { 10 System.out.println("msg = " + msg); 11 } 12 } 13 14 class MyException2 extends Exception { 15 public MyException2(String s) { 16 super(s); 17 } 18 } 19 20 public class E04_ExceptionClass { 21 public static void main(String args[]) { 22 try { 23 throw new MyException("MyException message"); 24 } catch(MyException e) { 25 e.printMsg(); 26 System.out.println("e.getMessage() = " + e.getMessage()); //輸出為null。 27 } 28 29 try { 30 throw new MyException2("MyException2 message"); 31 } catch(MyException2 e) { 32 System.out.println("e.getMessage() = " + e.getMessage()); 33 } 34 } 35 } 36 /*Output:msg = MyException message 37 * e.getMessage() = null 38 * e.getMessage() = MyException2 message 39 */
-----------------------------------------------------
printStackTrace()方法:堆棧跟蹤
我們知道在程序代碼執行的過程中,某個主線程可能會調用其他的代碼程序,調用執行之後,回來繼續執行主線程。這個過程中就需要堆棧來存儲調用時的斷點。(因為在嵌套調用的時候會需要存儲多個斷點,返回的時候再倒序依次返回,這要遵循後進先出的原則。)
堆棧跟蹤便可以看成是對代碼調用的跟蹤,當異常發生時可以根據該異常的printStackTrace()方法,打印出該異常的整個傳遞過程。
-----------------------------------------------------
一個方法裡如果會拋出異常,則必須在其方法後面聲明 throws 異常名 。如果不聲明則必須在這個方法裡通過 try{}catch{} 將異常處理掉。
一個方法可以聲明多個異常拋出 throws 異常1 , 異常2 ,但是在以後調用該方法時必須有多個catch來捕捉不同的異常。溫馨提示:往往在編程時,會把所有可能會有的異常(現在會有的和以後可能會有的)全都聲明在方法之後,這是一種習慣。因為在後期在對該方法進行功能擴展時可能會遇到出現這些異常,而當時在聲明這些異常之後編譯器會提示你將這些異常一一catch,所以這時你只需要填補catch裡的處理內容,這會來帶一定的便利。
下面是一個示例:
package exception; class OpenException extends Throwable {} class CloseException extends Throwable {} public class c { public static int open() { return -1; } public static void readFile() throws OpenException,CloseException { if(open() == -1) throw new OpenException(); } public static void main(String[] args) { try { readFile(); } catch (OpenException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CloseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }