項目中到處都是try-catch是一種常態嗎?


不知道什麼叫做「到處是」,問題太模糊了,沒法回答,我說點別的。

其實我是checked exception的支持者,Java的checked exception之所以那麼麻煩,是因為它沒法「自動傳播」。其實假如有這樣的功能:

void SomeMethod() auto throws

這樣SomeMethod方法可以自動根據內部實現來產生throws XyzException,大家就不會覺得很麻煩了。

這麼做的原因是,我認為異常也是方法簽名的一部分,讓編譯器控制各種異常拋出的情況,有利於寫出穩定的代碼。

當然,得認真去用,這不適合只知道偷懶的程序員。Java一邊想要編程能力不足的程序員,卻不知道這部分程序員是最喜歡偷懶的,於是checked exception只能被罵了。

所以大概還是得讓外部靜態工具來做這些事情吧,其實C#的Code Contract是個不錯的概念,但是實現的還不好,例如大大拖慢編譯速度簡直不可忍……


異常和錯誤碼各有適用範圍,總的來說異常適用範圍少,我j寫過想過答案,就不再貼一遍了


怎麼說呢,你不用errorcode就只能try-catch,但總的來說try-catch的數量要比if error == xxx then yyy else zzz要少多了吧。那你說到處都是if是不是常態?


首先,我假設題主問題的意思是:「在exception發生的地方都進行try-catch是不是常態?」。

那麼,開門見山地給你答案,到處都是try-catch不是常態,try-catch是個好東西,但你要懂得節制,不要縱慾過度,把try-catch搞得到處都是。只有在初級程序員那裡,到處都是try-catch才是常態。

既然說到try-catch,這是Java等一些高級語言才具備的特性,是先進生產力的發展方向,但這個特性是好是壞,廣大的程序員們,一直以來都存在爭議,因為要掌握它,用好它,也是一門學問。

一句話簡述try-catch的優勢:try-catch增加了程序的魯棒性。
而try-catch的劣勢是:它增加了程序語言的複雜度,事實上,正如題主存在的疑問一樣,try-catch的確並不總是被開發人員正確地使用,它常常被搞得到處都是,或者幾乎看不到,該用的地方不用,不該用的地方亂用,或者用對了地方,但用得莫名其妙;相應地,try-catch在一定程度上降低了程序開發的生產率;另外,try-catch會影響到編譯器對代碼的優化,一些情況下會降低代碼的執行效率。

在我看來,雖然上面在寫到try-catch的劣勢部分的時候手抖,打的字數比較多,但try-catch的優勢有足夠的分量,程序的穩定性往往更加重要,通過提高開發人員的水平,可以放大其優勢,而盡量避開其劣勢,因此,try-catch是個好東西。再回到題主的問題,到處進行try-catch不是常態,那如何正確使用try-catch,不要讓它到處都是呢?

和try-catch對應的,是throws。你們這些人啊,不要一看到exception,就忙著try-catch,或者throws,然後把exception批判一番。做人要有原則,遇到exception,你要考慮,你是否能夠合理地處理它,如果能,那就try-catch,否則throws,交給你的調用者去搞定,至於你的調用者怎麼去搞定,那不是你該關心的;如果你沒有能力處理exception,而你又把它給try-catch了,你的調用者就會被蒙在鼓裡,你讓你的調用者怎麼破,出了事故如何跟蹤錯誤。

一般地,通常地,不違背原則地,在業務底層碰上exception,throw之,而在業務上層遇到exception,try-catch之,所以,在業務底層,throws是常態,在業務上層,try-catch是常態。而exception的拋出,也要講技巧,exception分為static exception和runtime exception,static exception你必須進行check,runtime exception你可以不check,要根據期望,適時地使用runtime exception或者進行exception轉換,尤其對於那些可預期、可控的exception,盡量使用runtime exception自動向上傳遞,就不要使用static exception了,這樣也可以減少try-catch的數目,提高代碼的生產率。

至於遇上2B程序員,throws成性,一個try-catch都沒有,也是有的:
public static void main(String[] args) throws Exception;
try-catch成性的程序員就更多了,每寫一個方法,管你有沒有exception,把「萬能無敵」的try-catch亮出來先:
public void addCount(int count)
{
try
{
this.count += count;
}
catch(Exception e)
{
e.printStacktrace();
}
}

向上面這兩者這樣喪心病狂是要不得的,正確使用try-catch,該用才用,並且要把try-catch塊寫得盡量小,那些很健康,顯然不會出事故的代碼就不要往try-catch裡邊塞了。

舉個例子,一個登錄功能,在業務邏輯層定義了如下的登錄方法:
public boolean login(String user, String password);
在登錄時,登錄結果有成功和失敗,但登錄失敗的原因有多種,登錄過程中可能出現
IOException
UserNotExistException
PasswordNotMatchException
如果你在login方法中把這些exception給try-catch了,那麼調用者,業務表示層就只能簡單地提示用戶登錄失敗或者成功,在登錄失敗時,用戶無法知道登錄失敗的具體原因,體驗不好。因此,login方法的正確定義是:
public boolean login(String user, String password) throws IOException, UserNotExistException, PasswordNotMatchException;
這樣的話,exception被拋到了業務表示層,由業務表示層去try-catch,它知道怎麼去處理exception。
當然,某些囂張的開發人員很任性,老子就是要try-catch,我修改login方法的返回類型來表示多種登錄結果還不行么:
public final static int RS_LOGIN_SUCCESS = 0;
public final static int RS_LOGIN_ERROR_CONNECTION_REFUSED = -1;
public final static int RS_LOGIN_ERROR_USER_NOT_EXIST = -2;
public final static int RS_LOGIN_ERROR_PASSWORD_NOT_MATCH = -3;
public int login(String user, String password);

在這個例子中,通過throws或者修改返回類型都能解決問題,那麼問題來了,哪一種解決方案更好呢,嗯,返回值VS異常,哪些業務置於主事件流,哪些業務置於異常流?這是個可以進一步討論的問題!

另外還有try-for和for-try的問題,循環代碼需要進行try-catch時,是將一個try-catch包在循環外還是在循環中對每一次執行進行try-catch。這不能掉以輕心,需要根據業務情況處理,如果每一次執行相對獨立,那就應該for-try,如果整個循環有「原子性」,即一個執行失敗,需要整個for重來,那就要使用try-for。

編程中還有一種「任其崩潰」的思想,即認為我們不需要也不應該處理異常,程序出現了異常就應該讓它crash,也是很有道理的,需要根據編程語言的具體特性考慮,值得進一步討論,隱身術......


看具體情況。

首先,不用error code你就只能用異常。如果你的業務邏輯有很多出錯處理,那到處是try catch也正常。

其次,多數異常是checked,也就意味著在調用鏈里是向上「污染」(中性)的。這和error code不一樣,後者可以被有意忽略。這意味著,如果程序員不好好設計錯誤處理,異常機制下的代碼,會比error code機制混亂得多。


這涉及到語言的基因問題,

C++的代碼裡面,完全不推薦異常。

Java,你要是不用這個估計寸步難行。


就我而言,大多是情況都是調試的時候寫了,然後發布的時候懶得刪,所以……

╮(╯▽╰)╭……

滿滿都是懶惰。


取決於你用checked exception,還是unchecked exception,前者要不用try-catch,要麼聲明拋出。

反正在函數返回值中&>=0是正確,否則是各種錯誤碼不會更好。


java是常態,c++是病態


Try catch沒問題,關鍵在於你catch之後幹什麼,乾的還可以的話,可以按照自己喜歡的用。


當需要你在該層處理Exception的話並保證後續執行的話,就在該層catch了

如果你希望該層Exception由調用方處理或者一旦throw出後不會影響該層執行的話,就throw錯

總之,最後總有人要try catch掉所有層拋出的Exception。

(其實最後還有人幫你catch,VM,不過這等於程序跪掉了……)


到處都是try/catch肯定不是常態,有點工程經驗的都應該懂,哪怕是在JAVA這樣的語言中。

通常try/catch適用於以下場景:

1. 在代碼中對可預見而又無法掌控的情況進行處理。比如在SOCKET BIND時發現埠已經被佔用了、或者IO在打開文件時發現文件不存在,就需要在catch中做適當的處理避免程序crash掉;

2. 將問題向更上一層面傳遞,將處理權讓渡給caller。假如你寫了個ORM FRAMEWORK,在delete的時候發現外鍵關聯刪除失敗,FRAMEWORK不能擅自替上層的代碼決定該怎麼辦,於是只好把DB的報的錯誤原樣(或者加層外衣)throw出來,調用者根據業務需要選擇處理方式;

除此之外,所有問題應該由程序員主動判斷,就地解決。在規模比較大的軟體中,定義自己的Exception體系並正確、剋制地使用try/catch,可以讓代碼變得易讀易維護還美觀。

為什麼講「正確」並「克制」地使用?因為有些又蠢又懶的程序員喜歡這麼干:

1. 將函數所有代碼都放到try{}之中,哪怕 int i = 1這種賦值的都不放過。然後在catch里輸出一個錯誤信息就萬事大吉。這樣看起來是很省心哇,不用動腦子去分析哪裡可能發生什麼錯誤,反正所有錯誤都在catch的掌控之中;

2. 用try/catch來控制流程。舉個簡單的例子,假設有這麼個要求:

將字元串轉換成數字,並返回該數字的絕對值,如果出錯了就返回-1. 於是乎,就能見到類似下面代碼的奇葩做法:

int parse_number(const char* s){
try{
return abs(atoi(s));
}catch(Exception){
return -1;
}
}

這多省事兒,不用考慮s是不是NULL、不用考慮s是不是包含非數字的字元、不用考慮s是不是超出int的取值範圍...我是個優秀的程序員耶~~,我的代碼好簡潔。

try/catch和errno可以結合起來使用,二者不是非此即彼的關係,比如在某些場景下,可以將不確定的錯誤簡化歸納為固定的errno輸出,調用者直接檢查返回的errno即可,簡化了代碼,也減輕了負擔。比如某函數,成功返回0,失敗返回-1:

int foo(double d){
try{
do_something(d);
return 0;
}catch(Exception){
return -1;
}
}

void bar(double d){
int result = foo(d);
if(result == -1) return;
do_next_steps();
}


該try不try,不該try瞎try。遇到不負責的程序員就只能醉。


其實是程序員水平低下導致的

一般來說,是否拋出異常,拋出什麼異常必須要有比較清楚的認識

對於持久層,業務層,可以規定一類異常拋出,對於前端就不應該拋異常,異常再拋就給server容器或者客戶端了,這樣不合理

持久層和業務層可以拋出規定的異常,和一些無法攔截的系統異常,但如果是隱藏在實現內部的文件讀寫異常,則應該在內部截獲處理,因為拋出去上層也不知道怎麼處理

總得來說,過多的異常影響程序指令,導致程序性能下降,流程混亂,還是盡量避免使用


滿滿的都是Try catch肯定是不好的,尤其是在循環中....

沒有什麼理論依據,只是個人經驗以及想法。

另外,如果是開發過程中的throw 問題不大,可以幫助開發者更好的發現問題以及處理問題,一旦項目上線,客戶體驗是最重要的,不能隨便點點頁面,就跳轉Error頁面。可以邏輯判斷友情提示。試想一下你點畫面,隨便點點全是「親,頁面出錯了」云云,即使你配的圖片再可愛客戶也會煩。

更多的還是開發習慣,比如類型轉換,在textbox等控制項中就做足check功夫,不要調到後台之後又這的那的... 效率也會提高很多。


能處理的或者需要處理的catch,不能處理的拋出。


推薦閱讀:

malloc時出錯。提示 0xC0000374: 堆已損壞 (參數: 0x778CD8D0)什麼原因?
寫 patch 修復了一個軟體的 bug,卻被拒絕合併是一種怎樣的體驗?
如何看待能自動修復bug的CodePhage?
守望先鋒有哪些有趣的bug?
玩一些遊戲切換出去之後就無法再次運行的原因是什麼?

TAG:軟體 | 程序員 | Bug | 代碼 |