轉(zhuǎn)載于:http://www.tangrui.net/2007/01/09/difference-between-java-and-dotnet-on-exception-handling/
關(guān)于 Java 和 .Net 優(yōu)劣的爭(zhēng)論一直在繼續(xù),而在異常處理方面體現(xiàn)得最為激烈,因?yàn)樗麄冎g的差異是如此明顯。.Net 晚于
Java 出現(xiàn),那么 Java 對(duì) .Net 就理應(yīng)起到很重要的借鑒作用,但是偉大的 Anders Hejlsberg 為什么沒有繼續(xù)
Java
的實(shí)現(xiàn)方式,而是另辟蹊徑,這是一個(gè)非常值得研究的問題。因?yàn)槲覀円姓J(rèn)一個(gè)真理:正確的東西大家都是一樣的正確,錯(cuò)誤的卻各有個(gè)的錯(cuò)誤。可以肯定這不可
能是 Anders 的疏忽,那么他的道理究竟何在,或者說他們之間究竟有什么區(qū)別?
在你能耐下心來看完這篇帖子之前,我想要明確告訴你一個(gè)結(jié)論:Java 和 .Net 在異常處理的本質(zhì)上是沒有區(qū)別的。
一、Java 是如何處理異常的
如果一個(gè) Java 方法要拋出異常,那么需要在這個(gè)方法后面用 throws
關(guān)鍵字定義可以拋出的異常類型。倘若沒有定義,就認(rèn)為該方法不拋出任何異常。如果從方法的入口和出口的角度去考慮一下這個(gè)規(guī)范,我們知道參數(shù)可以認(rèn)為是方
法的入口(當(dāng)然某些情況下也可以是出口),而返回值則是方法的出口,這是在程序正常執(zhí)行的情況下,數(shù)據(jù)從入口入,出口出。要是程序非正常執(zhí)行,產(chǎn)生異常又
當(dāng)如何? 被拋出的異常應(yīng)該如何從方法中釋放出來呢? Java
的這種語法規(guī)范就如同給異常開了一個(gè)后門,讓異常可以堂而皇之“正確”地從方法里被拋出。
這樣的規(guī)范決定了 Java 語法必須強(qiáng)行對(duì)異常進(jìn)行 try-catch。設(shè)想一下,對(duì)于以下的方法簽名:
public void foo() throws BarException { ... }
暗含了兩方面的意思:第一,該方法要拋出 BarException 類型的異常;第二,除了 BarException
外不能拋出其他的異常。而正是這第二點(diǎn)的緣故,我們要如何保證沒有除 BarException 之外的任何異常被拋出呢? 很顯然,就需要
try-catch 其他的異常。也就是說,一般情況下,方法不拋出哪些異常就要在方法內(nèi)部 try-catch 這些異常。
Java 這樣的機(jī)制既有優(yōu)點(diǎn),也有缺點(diǎn)。先來說說優(yōu)點(diǎn):
- 很顯然,這種規(guī)范是由 Java 編譯器決定的。倘若 Java 程序的入口點(diǎn) main() 方法沒有任何異常拋出,就是說要在
main()
方法內(nèi)部,即整個(gè)程序內(nèi)部捕捉所有的異常,否則將無法通過編譯。這樣編譯器保證了程序?qū)γ總€(gè)異常都有相應(yīng)的計(jì)劃和處理,不會(huì)有未處理的異常被泄露到虛擬機(jī)
中,導(dǎo)致程序意外中斷或退出,也就是增強(qiáng)了程序的健壯性。當(dāng)然,Java 有 RuntimeException
的概念,這樣的異常仍然可以隨時(shí)被拋出到虛擬機(jī)中。
- 強(qiáng)行 try-catch 要求把異常作為程序設(shè)計(jì)的一部分看待。就如同方法的參數(shù)和返回值一樣,在編寫一個(gè)方法時(shí),要結(jié)合上下文做出通盤的考慮和打算。雖然異常是所謂的“意外情況”,但是這些“例外”理應(yīng)是被我們?nèi)苛私獠⑻幚淼摹?/li>
- 方便調(diào)試。異常理應(yīng)在正確的位置被捕捉。當(dāng)異常發(fā)生時(shí),我們能更清楚的了解到其來源和相應(yīng)處理程序的位置,而免去了在整個(gè)調(diào)用棧中摸索的麻煩。
- 在不借助任何文檔的情況下,從方法簽名就可以知曉應(yīng)該對(duì)哪些異常進(jìn)行處理。
Java
異常處理機(jī)制的這些優(yōu)點(diǎn)也直接導(dǎo)致了他的致命弱點(diǎn):將程序變得異常繁復(fù)。往往一個(gè)簡(jiǎn)單的程序,功能代碼寥寥幾行,而異常處理部分卻占用了程序的絕大部分篇
幅;同時(shí)導(dǎo)致縮進(jìn)深度加深,既不利于書寫,也不利于閱讀。另外他的強(qiáng)行 try-catch
需要程序員有更高深的造詣,能夠通盤考慮異常處理設(shè)計(jì)問題,這個(gè)在程序開始之初或者對(duì)于初學(xué)者是一個(gè)不小的門檻。這往往會(huì)阻礙其推廣與發(fā)展,因?yàn)榈退匠?
學(xué)者的信心往往因此而受到打擊。然而對(duì)于高手來說,編譯器是否能幫助他們找到未被處理的異常只是一個(gè)方便與否的問題,只要在編寫方法時(shí)注意了異常處理,即
便沒有編譯器的支持,情況也不會(huì)糟糕太多。反而倒是由于要遵循這樣復(fù)雜的異常處理規(guī)范,以至于大多數(shù)人都可能為了圖一時(shí)方便,對(duì)異常的基類型
Exception 或 Throwable
進(jìn)行籠統(tǒng)地捕捉,這樣做的危害就是那些你無法處理的異常被溺死在處理程序中,(按照異常處理原則,我們應(yīng)該只捕捉那些可以被處理或恢復(fù)的異常,而把其他的
異常繼續(xù)拋出。至于這樣做的優(yōu)勢(shì),以及不這樣做所帶來的問題,不是一兩句能夠說清楚,這里就不展開討論了。)導(dǎo)致程序的不穩(wěn)定和不確定。既沒有發(fā)揮
Java 語法在這方面的優(yōu)勢(shì),反而增加了憂患。
二、.Net 是如何處理異常的
一句話概括 .Net
的異常處理方式就是隨心所欲。沒有人要求你一定要拋出異常,也更沒有人要求你一定要捕捉異常。未被捕捉的異常會(huì)被以 Unhandled
Exception 的形式拋出到虛擬機(jī)中。在此我就要先解決一下文章開頭提到的問題,為什么說 Java 和 .Net
這兩種異常處理機(jī)制在本質(zhì)上是相同的。可以從兩個(gè)方面來考慮:
- 默認(rèn)情況下。Java 在默認(rèn)情況下 main() 方法是不拋出異常的,正如前面所說的,這要求所有的異常都必須在 main()
方法內(nèi)部被捕捉;而 .Net 則沒有這種約束,他的 Main()
以至于整個(gè)應(yīng)用程序中的任何一個(gè)方法對(duì)異常都是完全開放的。這樣來看,這兩者剛好是對(duì)立互補(bǔ)的。
- 非默認(rèn)情況下。Java 可以通過在 main() 方法后面加 throws 關(guān)鍵字使得整個(gè)應(yīng)用程序?qū)Ξ惓i_放;而 .Net
則可以通過給應(yīng)用程序域(Application Domain)的 UnhandledException
事件添加委托達(dá)到捕捉所有異常的目的,很顯然這又是對(duì)立互補(bǔ)的。
因此,就好像一個(gè)是“正反”,一個(gè)是“反正”,加在一起“正反反正”都是一樣的,對(duì)于達(dá)到控制異常的目錄來說,是沒有區(qū)別的。
很多 Java 愛好者都鄙視 .Net
的這種行為,一方面他令程序變得不夠健壯,因?yàn)槟J(rèn)情況下沒有強(qiáng)制的辦法要求所有的異常都被處理,或被正確處理;另外,他為調(diào)試增加了困難,不借助文檔或
代碼你將無法了解到一個(gè)方法可能拋出什么異常,而當(dāng)一個(gè)異常被拋出的時(shí)候,同時(shí)異常處理代碼又寫得不夠完善,你將不得不仔細(xì)查看整個(gè)調(diào)用棧來確定異常出現(xiàn)
的位置,而對(duì)于這一點(diǎn) Java 默認(rèn)是強(qiáng)制的。
但是 Anders 的想法總是有道理的。
- .Net
代碼寫起來非常容易。這是對(duì)于初學(xué)者,或者那些只是想實(shí)現(xiàn)一些測(cè)試性小功能的人而言,你完全沒有必要考慮太多異常處理的細(xì)節(jié),你要的就是寫代碼,然后讓他
跑起來。這樣的簡(jiǎn)單性無疑是你希望看到的,這樣的簡(jiǎn)單性無疑更有利于 .Net
在市場(chǎng)上的推廣。由于他在這方面并沒有什么理論上的漏洞,也就仍然適合構(gòu)建龐大的項(xiàng)目,只是感覺沒有那么舒服罷了。
- 一定程度上增加了程序的安全性。難道不捕捉異常可以被成為是安全的嗎?這個(gè)話也許要從另外一方面來想,前面說過,有些 Java
程序員(絕對(duì)不占少數(shù))為了圖省事,在強(qiáng)行捕捉異常的壓迫下,選擇捕捉異常的基類型,也就是捕捉所有的異常。這樣當(dāng)有你無法處理的異常出現(xiàn)時(shí),他們就溺死
在了你的代碼中,而外面的程序全然不知,還在以一種不確定的狀態(tài)運(yùn)行著,這就可能是危險(xiǎn)的開始。而如果是 .Net,那么 Unhandled
Exception 會(huì)被虛擬機(jī)捕獲,導(dǎo)致程序異常退出,雖然這從面子上對(duì)于用戶不是一個(gè)好的交代,但是深層次地他避免了程序在危險(xiǎn)的狀態(tài)下繼續(xù)運(yùn)行。
總之,蘿卜白菜各有所愛。我的這篇帖子力求公正地討論了這個(gè)問題,希望能對(duì)你有所幫助。
posted on 2007-12-03 10:17
Harriet 閱讀(1066)
評(píng)論(1) 編輯 收藏 所屬分類:
Java