許多程序員並沒有意識到一個錯誤和一個異常是有區別的,在出現問題時,這種區別對如何操作你的代碼有很重要的含意(見工具條,“簡介錯誤與異常”)。正如Mary Campione在The Java Tutorial(Java指南)中所寫的,“一個異常是在一個程序執行過程中出現的一個事件,它中斷了正常指令的運行。”根據American Heritage Dictionary的解釋,一個錯誤是“偏離了可接受的代碼行爲的一個動作或一個實例。”
那麼偏離(deviation)和中斷(disruption)有什麼不同呢?我們可以這麼來解釋:如果你正在一條路上駕駛,有人截住了你,這就是中斷。如果車發動不了了,那就是偏離(除非是我的車,我們認爲這種情況是normal的)。
這同Java有什麼關係呢?有很大的關係。Java有個很有趣的錯誤和異常層次關係(見圖1)。
的確,運用try {} catch (Exception e) {}的所有代碼只能找到一半你的錯誤。但是你是否應該截獲Throwable取決於你一旦截獲了它,你準備怎麼處理它。對Error的子集的快速瞭解可以讓你知道許多類的名字,如VirtualMachineError、ThreadDeath和LinkageError。在你打算截獲這些錯誤時,確信你要處理它們,因爲它們是嚴重的問題,所以是錯誤。
圖1. |
但ClassCastException不是一個錯誤嗎?的確不是。一個ClassCastException——或一種異常——只是VM(虛擬機)通知你的一種方式,通過這種方式,VM讓你知道,你(開發人員)已經犯了個錯誤,現在有一個機會來修改它。
另一方面,錯誤是VM的一個故障(雖然它可以是任何系統級的服務)。我們來引用JavaDoc對Error的定義:“Error是Throwable的一個子集,它指的是一個合理的應用程序不能截獲的嚴重的問題。大多數都是反常的情況。”
所以,錯誤是很難處理的,一般的開發人員(當然不是你)是不能理解處理這些錯誤的微妙之處的。那麼在你的工作中,當你覺得會產生一個足以被稱爲錯誤的一個事件時,該怎麼辦呢?
首先,記住錯誤是像異常一樣被拋出的,只有一點不同。拋出一個錯誤的方法不需要聲明它在做什麼(換句話說,異常是unchecked):
public void myFirstMethod() throws Exception //Since it's an exception, I have to declare //it in the throws clause { throw new Exception(); } public void mySecondMethod() //Because errors aren't supposed to occur, you //don't have to declare them. { throw new Error(); } |
注意有幾個異常是unchecked的,因此,其行爲就同錯誤一樣:NullPointerException、ClassCastException和IndexOutOfBoundsException都是RuntimeException的子類,RuntimeException及其所有的子集通常都是unchecked的。
那麼你應該怎麼處理這些討厭的unchecked的異常呢?你可以在它們可能出現的方法中截獲異常,但這種方法有很大的偶然性。這麼做可以解決一個問題,但是它會使其它unchecked的異常中斷代碼的其它部分。我們應該感謝ThreadGroup類提供的一個很好的辦法:
public class ApplicationLoader extends ThreadGroup { private ApplicationLoader() { super("ApplicationLoader"); } public static void main(String[] args) { Runnable appStarter = new Runnable() { public void run() { //invoke your application (i.e. MySystem.main(args)} } new Thread(new ApplicationLoader(), appStarter).start(); } //We overload this method from our parent //ThreadGroup , which will make sure that it //gets called when it needs to be. This is //where the magic occurs. public void uncaughtException(Thread thread, Throwable exception) { //Handle the error/exception. //Typical operations might be displaying a //useful dialog, writing to an event log, etc. } |
這個方法給我們的編程帶來了很大的改變。想想吧,過去當你在你的GUI中執行一個操作時,如果出現了一個unchecked的異常,你的GUI通常處於一種不正常的狀態(對話框仍然打開、按鈕不能激活,指針處於錯誤狀態),但是運用這種方法,你就可以使GUI回覆到其正常狀態,通知用戶所出現的錯誤,對此你會感覺良好,因爲你編寫了一個高質量的應用程序。
但這種技巧不僅僅用於GUI。運用過多資源的服務器應用程序可以用這種方法在全局釋放資源,通常避免VM進入一種不穩定的狀態。儘早並儘可能多地截獲錯誤、以明智的方法來處理它們,這是一個偉大的程序員和一個普通的程序員之間的不同。由於你已經閱讀了本文,你想成爲哪種程序員就是顯而易見的了。