Java異常的捕獲與處理

一、異常是什麼?
異常的定義:異常是導致一個程序中斷的指令流,一旦出現之後程序就立即退出。
例如:除數爲0

		int a = 10;
		int b = 0;
		System.out.println(a + "/" + b + "=" + a/b);
		System.out.println("運算結束");

程序運行結果:

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at ch1.Test1.main(Test1.java:8)

因此,一旦發生了異常後,程序將不再向下執行,直接退出程序.並輸出異常信息。
爲了保證程序中即使出現異常了可以處理異常並繼續運行,那麼就需要使用異常處理語句了。

二、怎麼處理異常?
1、在Java中處理異常的語句:

		try {
			System.out.println(a + "/" + b + "=" + a/b);
			//可能出現異常的語句
		}catch(異常類型 異常對象){
			//在這裏處理異常1
		}catch(異常類型 異常對象){
			//在這裏處理異常2
			//......可以捕獲多個異常對象
		} finally {
			//異常處理的統一出口,不管是否有異常、是否處理了異常都會執行
		}

2、因此可以使用try…catch…finally 語句處理剛剛出現的異常:

		int a = 10;
		int b = 0;
		try {
			//可能出現異常的語句
			System.out.println(a + "/" + b + "=" + a/b);
		}catch(ArithmeticException e){
			//在這裏處理異常
			System.err.println("數據有誤");
		} finally {
			System.out.println("執行了finally塊");
		}
		System.out.println("運算結束");

輸出結果爲:

數據有誤
執行了finally塊
運算結束

由於程序中添加了異常處理機制,所以此時的程序可以正常執行完畢,其中可以加入finally語句塊作爲統一的出口操作(不管是否有異常、是否處理了異常都會執行)。
3、finally用來做什麼(作用):
對於沒有垃圾回收機制和結構函數自動調用機制(析構函數:當對象不再被使用時會被調用的函數)的語言來說,finally語句非常重要,它能使程序員保證:無論try塊裏發生了什麼,內存總能得到釋放。但是Java有垃圾回收機制,所以內存釋放不再是問題,另外,java也沒有析構函數可供調用。那麼在什麼情況下才能用到finally呢?
當然是要把除內存之外的資源恢復到出事狀態時。這種需要清理的資源包括:已打開的文件或者網絡連接,在屏幕上畫的圖形,甚至可以是外部的某個開關。

三、異常的處理流程
異常的處理流程

  1. 當異常產生後,JVM自動生成一個異常類的實例化對象,如果此時編寫有了異常處理語句,則進行異常處理,否則交由JVM進行處理(即輸出異常信息並結束程序的執行)。
  2. 使用try語句捕獲異常對象後,自動與catch中的異常類型相匹配,如果匹配成功,則表示可以使用catch塊中的語句處理異常,如果都沒有匹配成功,則交由JVM進行處理。
  3. 如果存在finally語句塊,程序中不管是否出現了異常,都會執行該語句塊中的代碼。

四、異常的匹配
拋出異常對象後,異常處理機制會按照catch的順序匹配出“最近”的異常對象。找到匹配的處理程序之後,異常處理機制就認爲異常得到了處理,就不會繼續查找(區別於switch,switch需要break進行跳出匹配)。
值得注意的是:
1、查找的時候並不要求拋出的異常對象與處理程序所聲明的異常對象完全匹配,派生類的對象也可以匹配其基類(父類)的處理程序。就如:

		try {
			throw new NumberFormatException();//在此拋出一個異常對象
		} catch (Exception e) {
			e.printStackTrace();
		}

2、編譯器不允許把範圍大的異常對象放在前面,假如捕獲到了Exception異常對象,那麼就停止捕獲,而作爲其子類的處理程序永遠得不到執行。就如:

try {
			throw new NumberFormatException();
		} catch (Exception e) { //這裏編譯器報錯,從精確的異常匹配更大的異常。
			e.printStackTrace();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		}

五、throws 與 throw關鍵字

1、throws關鍵字作用於方法的聲明上,表示一個方法不處理異常,而交由調用處進行處理。

class MyMath {
	public int division(int num1, int num2) throws Exception {
		return num1/num2;
	}
}
public class MyMathTest {
	public static void main(String args[]) {
		//調用division(),如果不進行異常處理,則編譯不能通過(但是有特例)
		try {
			System.out.println(new MyMath().division(10,0));
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}

throws關鍵字不僅可以在普通方法上使用,mian方法上也可以使用。即mian方法不對異常進行處理,而向它的上級拋出,即由JVM進行處理。
實際上默認情況下所有異常都是交由JVM進行處理的,比如(一)中。

2、throw關鍵字:在程序中人爲拋出一個異常對象。

		try {
			throw new Exception("我自己拋的異常");
		} catch (Exception e) {
			e.printStackTrace();
		}
		/*
		 OutPut:
		 java.lang.Exception: 我自己拋的異常
			at ch1.Test1.main(Test1.java:22)
		 */

實際使用中較少。

3、throws 與 throw的區別:

  • throws:在方法的聲明上使用,表示此方法在調用時必須進行異常處理
  • throw:指的是在方法中人爲拋出一個異常類的實例化對象(這個對象可以是自定義的或者已經存在的)

六、RuntimeException——“不受檢查異常”
在(五、1)的代碼中就有註釋:如果不進行異常處理,則編譯不能通過(但是有特例),現在就來講這個特例。
引入:如果將一根字符串變爲整數類型,我們可以使用Integer類中的parseInt()方法實現。
查看Java API可知parseInt()方法:

public static int parseInt(String s, int radix) throws NumberFormatException

本方法存在了throws關鍵字的聲明,理論上講,在調用時必須進行異常處理,可實際的使用中可以不使用try…catch處理也可以編譯通過。

public class Test {
	public static void main(String[] args) {
		int a = Integer.parseInt("10");
	}
}

要想了解這個問題,就必須觀察NumberFormatException異常的繼承結構:
NumberFormatException異常的繼承結構
可見NumberFormatException是RuntimeException的子類,Java爲了異常的處理方便,定義出了一個特殊的異常類——RuntimeException,一旦拋出異常是此類或者此類的子類,那麼可以不用進行異常處理。如果不做任何處理,則一旦出現異常後將交由調用處理處進行處理。

RuntimException和Exception的區別:Exception必須處理,RuntimException可以不用處理。

七、異常處理模型
異常處理理論上有兩種基本模型:終止模型、恢復模型
1、終止模型:程序無法返回到異常發生的地方繼續執行,一旦異常被拋出,就表明錯誤已經無法挽回,也不能回來繼續執行。
2、恢復模型:意思是在異常處理中修正錯誤,然後重新嘗試調用出問題的代碼或者方法,並認爲第二次能成功。對於恢復模型,通常系統異常被處理之後能繼續執行程序,如果要實現該模型,那麼在遇到錯誤時不能拋出異常,而是調用方法來修正該錯誤。或者,把try塊放在while循環裏面,並在try塊後添加break退出語句(直到達到滿意的結果即退出)。

異常表面上看,恢復模型比終止模型好。但實際上程序員更喜歡使用“終止模型”的代碼,而忽略恢復的行爲。其中主要原因可能是它所導致的耦合:恢復性的處理程序需要了解異常拋出的地點,這將依賴於拋出位置的非通用性代碼,這增加了代碼編寫和維護的困難,更別說異常可能從許多地方拋出的大型程序。

八、總結:
異常是Java程序設計不可分割的一部分,如果不瞭解如何使用它們,那你只能完成很有限的工作。我們不應把Java的異常處理機制當成是單一用途的工具。是的,他被設計出來處理一些煩人的運行時錯誤,這些錯誤往往是由代碼控制能力之外的因素導致的。然而,它對於發現某些編譯器無法檢測到的錯誤,也是非常重要的。

注:個人知識有限,文章有錯誤或者不足之處歡迎指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章