全面理解java異常機制


      在理想狀態下,程序會按照我們預想的步驟一步一步的執行,但是即使你是大牛,你也不可避免出錯,所以java為我們提供了異常機制。本文將會從以下幾個方面介紹java中的異常機制:
  • 異常機制的層次結構
  • 異常的處理過程
  • 拋出異常
  • 捕獲異常
  • 異常機制的實現細節

一、異常機制的層次結構
      在java程序設計語言中,所有的異常對象都是派生於Throwable類,一般情況下,如果java中內置的異常類不能滿足需求,可以自定義異常類只需要繼承與Throwable類即可,下面是java 中的異常層次結構:
這里寫圖片描述
      error類表示java內部錯誤,例如jvm出錯或者內存不足等,這層一般不用我們關心(其實我們也無可奈何,如果出現此種問題),一旦出現此種錯誤程序會自動結束。
      我們主要還是需要研究Exception類及其子類,Exception主要有兩個子類,IOException和RuntimeException我們一個個看。IOException是IO錯誤類問題導致的異常,是可預知的,例如由於文件不存在而打開文件失敗引起的異常等。RuntimeException類異常表示運行時異常,例如數組下標越界,訪問空指針等,是不可預知的,但是這類異常於編寫程序時應當予以避免,例如你可以使用if(a==null).....,加以檢查避免。
      以上從一種角度對這些主要的異常類進行了分類,其實我們還有一種分類方式,按照是否是檢查類異常(checked)進行分類,什么是檢查類異常?檢查類異常(checked)就是指編譯器會檢查當前的代碼塊中,判斷是否有這么一條語句在程序執行時可能產生異常,如果有就會建議程序員處理。(實際上你必須處理,不然編譯不會通過),在以上的分層中,error類和RuntimeException類屬於非檢查異常類(unchecked),而IOException屬於檢查類異常。


二、異常的處理過程

public class Test2 {
	public static void main(String[] args){
		doMaths();
	}
	public static void doMaths(){
		int a = 10/0;//此處將會出現異常
		System.out.println(a);
	}
}

輸出結果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at test.Test2.doMaths(Test2.java:8)
	at test.Test2.main(Test2.java:5


      相信大家在看完上文之后,可以判斷出此處的異常應該屬於RuntimeException,屬於unchecked異常,由輸出的結果可以看出:main方法中調用方法doMaths();,於是進入該方法內部,執行int a = 10/0;產生異常,在本方法中未找到處理,於是返回調用處(即main函數中),發現main函數中依然沒有處理方法,於是執行默認操作(打印異常信息)。
      我們的程序難免會出現問題,但是絕對不能讓用戶看出來你的程序出現問題,所以這些異常信息是絕對不能讓用戶看見的,且不說他是否能看懂,一旦遇到“行家”,你的程序就會存在安全隱患,由此可見,對異常的處理是多么重要。下面我們將討論兩種處理異常的機制。


三、拋出異常
      以下所討論的處理異常的方法,主要還是針對檢查類異常(checked),因為對於error類的錯誤我們沒法處理,對於RuntimeException類異常我們應當盡量在程序中加以避免,所以我們主要還是對檢查類異常進行處理。
      我們使用關鍵字throws將本程序中可能遇到的異常向上拋出(向調用出拋出,讓調用者處理),而本身不做處理。

public void test() throws AppException{
	................
	...............
}


      throws 緊跟方法的括號后面,這個叫異常的聲明表示本方法不處理這個異常,誰調用我這個方法誰來處理(后面將討論如何處理異常,因為總要有人來處理,否則就默認打印異常信息),可以聲明多個異常,異常之間使用逗號相隔。
      那么throw關鍵字和throws關鍵字究竟有什么聯系呢?throw可以理解為是在throws關鍵字的主動行為。先看代碼:

public class Test2 {
	public static void main(String[] args){
		show();
	}
	public static void show(){
		String s = null;
		s.charAt(0);
	}
}
/*以上代碼拋出NullPointerException,空指針異常*/
public static void show() throws NullPointerException{
		String s = null;
		s.charAt(0);
	}
/*你可以選擇拋給調用者*/
public static void show(){
		String s = null;
		if(s==null){
			throw new NullPointerException();
		}
		s.charAt(0);
	}
/但是如果你可以判斷s處可能出現異常,就可以主動拋出異常*/
      小結一下,throws關鍵字表示:本函數中存在某個異常但是我不知道,如果出現此異常就拋給調用者。throw一般和判斷語句if配合的多表示:如果滿足條件就結束此函數並向調用者拋出異常,否則就繼續執行接下來的代碼。 **四、捕獲異常**       以上說了那么多,我們知道如何將遇到的異常拋出,讓別人處理,但是總是有人需要處理這個異常的,總不能讓系統默認的將異常信息打印出來吧!再者說,一個strong的程序必須要有對錯誤的處理,讓程序控制在手中而不是莫名卡死或者退出。接下來,我們一起探討如何捕獲異常對他進行處理。       捕獲異常,我們使用try{}catch{}關鍵字,try中代碼表示可能出現異常的代碼塊,catch中的代碼塊表示捕捉到該異常之后需要進行的后續操作。
/*使用上述代碼,添加try catch捕獲異常*/
public class Test2 {
	public static void main(String[] args){
		try{
			show();//可能出現異常的代碼塊
		}catch(NullPointerException z){
			System.out.println("throw Exception");
			//捕捉到異常后的操作
		}
	}
	public static void show()throws NullPointerException {
		//將異常拋給調用者
		String s = null;
		s.charAt(0);
	}
}

輸出結果:
throw Exception
      對比於之前,打印自己控制的內容是不是比默認打印異常信息更加的友好?我們常常會遇到這么一個問題:打開一個文件,准備對文件進行操作,但是在對文件的操作過程中出現異常,退出程序了,但是文件並沒有被顯式的關閉,造成資源浪費。       我們常常使用關鍵字finally來完成類似這樣的操作。finally關鍵字緊跟catch后面,表示無論是否發生異常都會執行的代碼塊。finally不是必須的可以沒有。
try{
  //可能出現異常的代碼塊
}catch{
  //捕獲異常之后的代碼塊
}finally{
  //肯定要執行的代碼塊
}
**五、異常機制的實現細節**       第一個我想說的是try{}catch{}這種結構並不是唯一的,可以由多個catch語句的,對多個不同的異常進行捕獲。       第二個很重要的finally這個關鍵字中如果出現return等關鍵字程序的接下來的執行順序問題。例如:
try{
  int a=0;
  //出現異常
  return a;
}catch(..){
 a=1;
 return a;
}finally{
 a=2;
 return a; 
}
程序返回:2;
      能想明白么?其實不難,只要知道finally語句塊中是最后執行的就可以理解了,當try中出現異常,被catch接住,將a賦值為1,在返回之前跳轉到finally語句塊中,最后在finally語句塊中返回2,這種在finally語句塊中有return語句的會阻止catch中的return執行。       所以,我們建議finally語句塊中不要使用return語句。        **以上便是java異常機制的基本內容,如果錯誤,望大家指出。tk**


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM