-----------------
異常處理是Java編程中非常重要的一個(gè)部分.建議在使用異常之前閱讀<Effective Java Programming Language Guide>或者<Practical Java>.
下面從書中摘出幾條建議:
*絕對不要忽略異常
*千萬不要隱藏異常***
*僅在不正常的情況下使用異常
*對可恢復(fù)的情況使用可檢查異常,對程序錯(cuò)誤使用運(yùn)行時(shí)異常(RunTimeException)
*給方法引發(fā)的異常做文檔
*在詳細(xì)信息里面包括失敗捕獲信息
*使用finally避免資源泄漏
*....
在這里特別提出的是,在開發(fā)中要特別處理NULL的情況,否則經(jīng)常引發(fā)NullPointException異常,在Java里這是一個(gè)最令人頭疼的異常了.
如果你的程序因?yàn)橐粋€(gè)NULL值,而報(bào)了幾十個(gè)NullPointException的話,不但得讓人煩死,而且還非常難以找到錯(cuò)誤所在.所以在Java中一定要注意這個(gè)問題.
如果你的函數(shù)不允許Null值,那么可以截獲它,拋出一個(gè)異常,或者給客戶更友好的提示,難道不好嗎?
讓我們來看一個(gè)例子:
public String getName(User aUser) { //如果aUser為Null,會發(fā)生什么情況 return aUser.getName(); } |
很明顯,如果參數(shù)為Null,就會拋出異常.應(yīng)該改為:
public String getName(User aUser) { if(null=aUser) { return ""; } else { return aUser.getName(); } } |
或者你要求參數(shù)不能為空,還可以拋出一個(gè)異常,強(qiáng)制使用者不能傳入空值.
還有經(jīng)常被忽略的是RunTimeException和普通異常的區(qū)別,在Java中,這是一個(gè)特殊的異常類,程序中如果遇到這個(gè)異常,用戶可以不截獲它,而如果是其他的普通異常,就不許要截獲它.我們的代碼經(jīng)常這么寫:
try { //your code here } catch(Exception e) { //do warn } |
這樣寫的話,就截獲了所有異常,當(dāng)然也包括了RunTimeException. 在很多情況下,這是不合適的處理方式,我們只應(yīng)截獲必要的異常,而應(yīng)該忽略RuntimeException.
關(guān)于RunTimeException,在Spring中還有更好的利用方式,建議閱讀Spring框架中在事務(wù)中對異常的處理代碼,例如對Jdbc拋出的SqlException的轉(zhuǎn)換.
關(guān)于異常處理,我提出幾點(diǎn)建議:
*捕獲異常而且再次拋出時(shí)要包含原來的異常信息
*不要忘了RunTimeException,除非必要,否則不要用catch(Exception e)的方式捕獲所有異常.
*不要用異常做流程控制,異常的性能代價(jià)比較高昂.(對此,可能有人不同意.此處不詳細(xì)討論)
*不要把異常處理都拋給別人,本函數(shù)有能力處理的就不要拋出.
在此建議讀者詳細(xì)閱讀<Effective Java Programming Language Guide>或者<Practical Java>.
【過度依賴】
在定位錯(cuò)誤的時(shí)候,經(jīng)常遇到瀏覽了七 八個(gè)文件還是沒有找到什么地方執(zhí)行了真正需要的函數(shù),這個(gè)時(shí)候就非常郁悶.A調(diào)用了B,B調(diào)用了C,C調(diào)用了D......讓人找不到北
面對這樣的程序,存在的問題不僅僅是定位錯(cuò)誤麻煩,而且如果需要維護(hù)這樣的函數(shù)庫/框架,恐怕你的有非常高的統(tǒng)御能力才行,否則打死我也不去維護(hù).
那么我們自己最好不要寫這樣的程序出來給人用.
【濫用接口】
現(xiàn)在流行"面對接口編程",這本身本來是不錯(cuò),但是濫用接口的現(xiàn)象卻經(jīng)常發(fā)生.
"面向接口",于是所有的類都有一個(gè)對應(yīng)的接口,接口的函數(shù)聲明和類一模一樣,而且一個(gè)接口只有一個(gè)類來實(shí)現(xiàn)它.這樣的面向接口有什么意義哪? (為了用Spring的事務(wù)的情況除外)
根據(jù)"迪比特法則(Law of Demter)",一個(gè)對象應(yīng)當(dāng)對其他對象有盡可能少的了解.一個(gè)接口內(nèi)應(yīng)該只定義對方所需要的方法,而不要把一些沒用的方法聲明放在接口里面.
例如如下一個(gè)類:
public class MyCounter { private int n1; private int n2; public MyCounter(int n1,int n2) { this.n1=n1; this.n2=n2; } public void setN1(int n1) { return this.n1 = n1; } public void setN2(int n2) { return this.n2 = n2; } public int getN1() { return n1; } public int getN2() { return n2; } public int getResult() { return n1 + n2; } } |
我們可以看到,這個(gè)類的主要目的是得到計(jì)算結(jié)果,所以正確的接口應(yīng)該類似:
public interface Counter { int getResult(); } |
public interface Counter { int getResult(); int getN1(); int getN2(); void setN1(int n1); void setN2(int n2); } |
我們想一想,這樣做有2個(gè)后果:
1.除了getResult之外,其他的函數(shù)我們根本用不到,所以是多余的.
2.如果我們要自己實(shí)現(xiàn)一個(gè)Counter,如果接口中僅僅定義了getResult,我們僅僅需要實(shí)現(xiàn)它就可以了.我們自己的類可能是多個(gè)數(shù)運(yùn)算,有乘除加減等等各種運(yùn)算,參數(shù)也有可能是一些數(shù)組.但是如果按照第二種方法聲明接口的話,我們就必須實(shí)現(xiàn)后面的四個(gè)方法,如果這樣的話,實(shí)現(xiàn)這樣?xùn)|西不僅沒用,而且浪費(fèi)時(shí)間.我們恐怕要大聲罵娘了吧.
所以,接口有好的作用,但是不要濫用.
■ 如果你的接口永遠(yuǎn)只有一個(gè)類實(shí)現(xiàn),那么可能就沒有必要用接口.
■ 你的接口只需要聲明別人用到的函數(shù)即可.