【java基礎(四十六)】異常(二)聲明異常、拋出異常、創建異常

聲明受查異常

如果遇到了無法處理的情況,那麼Java的方法可以拋出一個異常。這個道理很簡單:一個方法不僅需要告訴編譯器將要返回什麼值,還要告訴編譯器有可能發生什麼錯誤。例如:一段讀取文件的代碼知道有可能讀取的文件不存在,或者內容爲空,因此,試圖處理文件信息的代碼就需要通知編譯器可能會拋出IOException類的異常。

方法應該在其首部聲明所有可能拋出的異常。這樣可以從首部反映出這個方法可能拋出哪類受查異常。例如,下面是標準類庫中提供的FileInputStream類的一個構造器的聲明。

public FileInputStream(String name) throws FileNotFoundException

這個聲明表示這個構造器將根據給定的String參數產生一個FileInputStream對象,但也有可能拋出一個FileNotFoundException異常。如果發生了這種糟糕的情況,構造器將不會初始化一個新的FileInputStream對象,而是拋出一個FileNotFoundException類對象。如果這個方法真的拋出了這樣一個異常對象,運行時系統就會開始搜索異常處理器,以便知道如何處理FileNotFoundException對象。

在自己編寫方法時,不必將所有可能拋出的異常都進行聲明。至於什麼時候需要在方法中用throws子句聲明異常,什麼異常必須使用throws子句聲明,需要記住在遇到下面4中情況時應該拋出異常:

  • 調用一個拋出受查異常的對象,例如,FileInputStream構造器。
  • 程序運行過程中發現錯誤,並且利用throw語句拋出一個受查異常。
  • 程序出現錯誤,例如,a[-1] = 0會拋出一個ArrayIndexOutOfBoundsException這樣的非受查異常。
  • Java虛擬機和運行時庫出現的內部錯誤。

如果出現前兩種情況之一,則必須告訴調用這個方法的程序員有可能拋出異常。爲什麼?因爲任何一個拋出異常的方法都有可能是一個死亡陷阱。如果沒有處理器捕獲這個異常,當前執行的線程就會結束。

對於那些可能被他人使用的Java方法,應該根據異常規範(exception specification),在方法的首部聲明這個方法可能拋出的異常。

class MyAnimation {

	public Image loadImage(String s) throws IOException {
		...
	}

}

如果一個方法有可能拋出多個受查異常類型,那麼就必須在方法的首部列出所有的異常類。每個異常類之間用逗號隔開。如:

class MyAnimation {

	public Image loadImage(String s) throws FileNotFoundException, EOFException {
		...
	}

}

但是,不需要聲明Java的內部錯誤,即從Error繼承的錯誤。任何程序代碼都具有拋出那些異常的潛能,而我們對其沒有任何控制能力。

同樣,也不應該聲明從RuntimeException繼承的那些非受查異常。如:

class MyAnimation {

	void drawImage(int i) throws ArrayIndexOutOfBoundsException { // 這樣是不對的
		...
	}
}

這些運行時錯誤完全在我們的控制之下。如果特別關注數組下標引發的錯誤,就應該將更多的時間花費在修正程序中的錯誤上,而不是說明這些錯誤發生的可能性上。

總之,一個方法必須聲明所有可能拋出的受查異常,而非受查異常要麼不可控制(Error),要麼就應該避免發生(RuntimeException)。如果方法沒有聲明所有可能發生的受查異常,編譯器就會發出一個錯誤消息。

當然,從前面的示例中可以知道:除了聲明異常之外,還可以捕獲異常。這樣會使異常不被拋到方法之外,也不需要throws規範。稍後,將會討論如何決定一個異常是被捕獲,還是被拋出讓其他的處理器進行處理。

如果類中的一個方法聲明將會拋出一個異常,而這個異常是某個特定類的實例時,則這個方法將有可能拋出一個這個類的異常,或者這個類的任意一個子類的異常。例如,FileInputStream構造器聲明將有可能拋出一個IOException異常,然而並不知道具體是哪種IOException異常。它既可能是IOException異常,也坑你是其子類的異常。如:FileNotFoundException。

拋出異常

假設在程序代碼中發生了一些很糟糕的事情。一個名爲readData的方法正在讀取一個首部具有下列信息的文件:
Content-length: 1024
然而,讀到733個字符之後文件就結束了。我們認爲這是一種不正常的情況,希望拋出一個異常。

首先要決定應該拋出什麼類型的異常。將上述異常歸結爲IOException是一種很好的選擇。仔細地閱讀Java API文檔之後會發現:EOFException異常描述的是“在輸入過程中,遇到了一個未預期的EOF後的信號”。這正是我們要拋出的異常。下面是拋出這個異常的語句:

throw new EOFException();

或者

EOFException e = new EOFException();
throw e;

和代碼放在一起:

String readData(Scanner in) throws EOFException {
	while (...) {
		if (!in.hasNext()) {
			if (n < len)
				throw new EOFException();
		}
	}
}

EOFException 類還有一個含有一個字符串型參數的構造器。這個構造器可以更加細緻的描述異常出現的情況。

String gripe = "Content-length:" + len + ", Receive:" + n;
throw new EOFException(gripe);

在前面已經看到,對於一個已經存在的異常類,將其拋出非常容易。

  • 找到一個合適的異常類。
  • 創建這個類的一個對象。
  • 將對象拋出。

一旦方法拋出了這個異常,這個方法就不可能返回到調用者。也就是說,不必爲返回的默認值或錯誤代碼擔憂。

創建異常類

在程序中,可能會遇到任何標準異常類都沒有能夠充分地描述清楚的問題。在這種情況下,創建自己的異常類就是一件順利成章的事情了。我們需要做的只是定義一個派生於Exception的類,或者派生於Exception子類的類。例如,定義一個派生於IOException的類。習慣上,定義的類應該包含兩個構造器,一個是默認的構造器;另一個是帶有詳細描述信息的構造器(超類Throwable的toString方法將會打印出這些詳細信息,這在調試中非常有用)。

class FileFormatException extends IOException {
	public FileFormetExcetpion() {};
	public FileFormatExcetpin(String gripe) {
		super(gripe);
	}
}

現在,就可以拋出自己定義的異常類型了。

String readData(BufferedReader in) throws FileFormatException {
	while (...) {
		if (ch == -1) {
			if (n < len)
				throw new FileFormatException();
		}
	}
}

捐贈

若你感覺讀到這篇文章對你有啓發,能引起你的思考。請不要吝嗇你的錢包,你的任何打賞或者捐贈都是對我莫大的鼓勵。

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