目前,存在着大量有關如何恰當地使用異常機制的爭論。有些程序員認爲所有的已檢查異常都很令人厭惡;還有一些程序員認爲能夠拋出的異常量不夠。我們認爲異常機制有其用武之地。下面給出使用異常機制的幾個技巧。
異常處理不能代替簡單的測試
作爲一個示例,在這裏編寫一段代碼,試着上百次地對一個空棧進行退棧操作。在實施退棧操作之前,首先要看棧是否爲空。
if (!s.empty()) s.pop();
接下來,強行進行退棧操作。然後,捕獲EmptyStrackException異常來告知我們不能這樣做:
try {
s.pop();
} catch (EmptyStractException e) {
}
與執行簡單的測試相比,捕獲異常所花費的時間大大超過了前者。因此使用異常的基本規則是:只在異常情況下使用異常機制。
不要過分地細化異常
很多程序員習慣將每一條語句都分裝在一個獨立的try語句塊中。
PrintStream out;
Strack s;
for (int i = 0; i < 100; i++) {
try {
n = s.pop();
} catch (EmptyStrackException e) {
...
}
try {
out.writeInt(n);
} catch (IOException e) {
...
}
}
這種編程方式將導致代碼量的急劇膨脹。首先看一下這段代碼所完成的任務。在這裏,希望從棧中彈出100個數值,然後將它們存入一個文件中。如果棧是空的,則不會變成非空狀態;如果文件出現錯誤,則也很難給予排除。如果出現上述問題後,這種編程方式無能爲力。因此,有必要將整個任務包裝在一個try語句塊中,這樣,在任何一個操作出現問題時,整個任務都可以取消。
try {
for (i = 0; i < 100; i++) {
n = s.pop();
out.writeInt(n);
}
} catch (IOException e) {
...
} catch (EmptyStrackException e) {
...
}
這段代碼看起來清晰多了。這樣也滿足了異常處理機制的其中一個目標,將正常處理與錯誤處理分開。
利用異常層次結構
不要只拋出RuntimeException異常。應該尋找更加適當的子類或創建自己的異常類。
不要只捕獲Throwable異常,否則,會使程序代碼更難度、更難維護。
考慮受查異常和非受查異常的區別。已檢查異常本來就很龐大,不要爲邏輯錯誤拋出這些異常。
將一種異常轉換成另一種更適合的異常時不要猶豫。例如,在解析某個文件中的一個整數時,捕獲NumberFormatException異常,然後將它轉換成IOException或MySubsystemException的子類。
不要壓制異常
在Java中,往往強烈地傾向關閉異常。如果編寫了一個調用另一個方法的方法,而這個方法有可能100年才拋出一個異常,那麼,編譯器會因爲沒有將這個異常列在throws表中產生抱怨。而沒有將這個異常列在throws表中主要出於編譯器將會對所有調用這個方法的方法進行異常處理的考慮。因此,應該將這個異常關閉:
public void loadImage(String s) {
try {
...
} catch (Exception e) {
...
}
}
現在,這段代碼就可以通過編譯了。除非發生異常,否則它將可以正常的運行。即使發生了異常也會被忽略。如果認爲異常非常重要,就應該對它們進行處理。
在檢測錯誤時,“苛刻”要比放任更好
當檢測到錯誤的時候,有些程序員擔心拋出異常。在用無效的參數調用一個方法時,返回一個虛擬的數值,還是拋出一個異常,哪種處理方式更好?例如,當棧空時,Strack.pop是返回一個null,還是拋出一個異常?我們認爲:在出錯的地方拋出一個EmptyStrackException異常要比在後面拋出一個NullPointerException異常要好。
不要羞於傳遞異常
很多程序員都感覺應該捕獲拋出的全部異常。如果調用了一個拋出異常的方法,例如,FileInputStream構造器或readline方法,這些方法就會本能的捕獲這些可能產生的異常。其實,傳遞異常要比捕獲這些異常更好。
讓高層次的方法通知用戶發生了錯誤,或者放棄不成功的命令更加適宜。
捐贈
若你感覺讀到這篇文章對你有啓發,能引起你的思考。請不要吝嗇你的錢包,你的任何打賞或者捐贈都是對我莫大的鼓勵。