人在江湖

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            82 Posts :: 10 Stories :: 169 Comments :: 0 Trackbacks

          這篇和前面的 總結java的interface和abstract class 一樣。跳過最基礎語法不聊,只說一些比較tricky的東西和一些好的practice.

           

          語法:

          Exception繼承自Throwable. Throwable還有一個兒子是Error,但是一般用不到。不過有些二百五面試官喜歡問回字有幾種寫法,所以還是要知道有Error這回事的。Exception分為checked和unchecked兩種。

           

          java的checked exception一直是個很有爭議的東西。Thinking in Java的作者Bruce, C#的設計者和Martin Fowler都對checked exception持質疑態度。 一個典型的反對理由是:"We felt it was unrealistic to require the programmer to provide handlers in situations where no meaningful action can be taken." 這話太實惠了。寫方法給caller調用,怎么能magically知道調用這個方法的所有caller,能夠處理特定的異常呢?

           

          關于Exception有個比較基本的語法是,子類override的方法如果聲明拋出exception, 只能拋出父方法聲明的exception,或者那個exception的子類。需要注意的是,“回”字還有一種寫法,對于constructor來說沒有這個限制,子類可以拋出任意exception。父類構造函數聲明的exception,子類也必須聲明,而且子類的構造函數不能捕捉父類聲明的exception. 這個想想也容易理解,父類構造出錯了,兒子居然能處理還把自己生出來了,沒老子哪來的兒子?當然你可以抬杠說老子戴綠帽子的情況。

           

          實踐:

          1. 盡量不要在構造函數里做復雜的操作,盡量不要讓constructor拋出exception。如果在構造函數里拋出exception,需要用nested try block. 如下:

             1: public class Cleanup { 
             2:   public static void main(String[] args) { 
             3:     try { 
             4:       InputFile in = new InputFile("Cleanup.java"); 
             5:       try { 
             6:         String s; 
             7:         int i = 1; 
             8:         while((s = in.getLine()) != null) 
             9:           ; // Perform line-by-line processing here... 
            10:       } catch(Exception e) { 
            11:         System.out.println("Caught Exception in main"); 
            12:         e.printStackTrace(System.out); 
            13:       } finally { 
            14:         in.dispose(); 
            15:       } 
            16:     } catch(Exception e) { 
            17:       System.out.println("InputFile construction failed"); 
            18:     } 
            19:   } 
            20: } 

          而不是用finally來做清理工作。

           

          2. exception的一個基本使用原則是,exception不是設計用來控制程序flow的。 這是很簡單的道理,還是引用effective java的一個例子吧

             1: // Horrible abuse of exceptions. Don't ever do this!
             2: try {
             3: int i = 0;
             4: while(true)
             5: range[i++].climb();
             6: } catch(ArrayIndexOutOfBoundsException e) {
             7: }

          我真正要說明的是,上面說的原則很對,但是走到極端就不對了。有的人為了 不用exception控制程序flow, 就寫一大堆的if…else語句試圖考慮各種情況,正好前不久有同事說了個笑話,我覺得可以輔助解釋這個問題。

          =============================

          某日,老師在課堂上想考考學生們的智商,就問一個男孩“樹上有十只鳥,開槍打死一只,還剩幾只?”

          男孩反問“是無聲手槍,還是其他沒有聲音的槍么?”

          “不是。”

          “槍聲有多大?”

          “80~100分貝。”

          “那就是說會震的耳朵疼?”

          “是。”

          “在這個城市里打鳥犯不犯法?”

          “不犯。”

          “您確定那只鳥真的被打死啦?”

          “確定.”老師已經不耐煩了,”拜托,你告訴我還剩幾只就行了,OK?”

          “OK.鳥里有沒有聾子?”

          “沒有。”

          “有沒有鳥智力有問題,呆傻到聽到槍響不知道飛的?”

          “沒有,智商都在200以上!”

          “有沒有關在籠子里的?”

          “沒有。”

          。。。

          ==============================

          后面還有一堆“例外”情況。我們寫程序總不能真寫成

          if(鳥是聾子)

          else if(鳥是傻子)

          else if(鳥是瘸子)

          原則應該是,如果一些情況確實是 “例外情況”,就用exception處理吧。不要很勤奮地寫一堆defensive的判斷。我們不會有故事里的小男孩兒思維那么滴水不漏的。別把Java程序退回c語言了。另一個例子是FileNotFoundException, java I/O沒有讓你每次用文件都提前調用exists()檢查一下,我想原因不光是 檢查文件的那一毫秒文件存在,run到下一步的時候,下一毫秒文件消失了,Sun沒覺得你人品那么壞吧。理念仍然是,如果你覺得文件肯定存在,你就直接用吧,一旦不存在,你再另外當成異常情況處理。不要讓一堆if…else弄臟了程序。

           

          3. exception有個典型用法是在方法體中,進行參數合法性校驗

           

             1: public BigInteger mod(BigInteger m) {
             2: if (m.signum() <= 0)
             3: throw new ArithmeticException("Modulus <= 0: " + m);
             4: ... // Do the computation
             5: }

          也有很多人用assert語句判斷, 比如 Assert.notNull(object)。手動拋exception可以拋特定的類型,assert語句更方便。可以根據實際情況取舍。

           

          4. 既然上面說了checked exception本身是java設計不太合理的地方。我傾向于說,應該及時把checked exception translate成unchecked. 我知道exception的處理原則有一條是,如果你不知道怎么處理它,就不要捕捉它。 對于checked exception來說,它總force你去處理,太討厭了。如果caller不知道怎么處理,留著給更上層的程序處理…底層的程序都不會處理,一般來說上層的程序就更不知道該怎么處理了,那還不如在盡量底層的調用中,要么處理它(這種情況很少,打log之類的算不上“處理”),要么就轉成RuntimeException拋上來, 消滅掉checked exception帶來的burden. 注意:translate exception的過程中,不要扔了原來的exception, 而要把它放在exception constructor的argument里, new RuntimeException(e)。這是很基本的東西,不多說。

           

          5. 不要吞exception. 這個太基礎,不多說。

           

          6. 見到exception要把它記log里,而不是簡單print stack一下,log4j的api有可以接受Throwable作為參數的。

           

          7. 每層拋出來的exception要對當前這一層有意義,比如persistence層出問題,UI上你告訴客戶hibernate的session關閉了,不能繼續load數據了,客戶還以為你的程序怎么跟狗熊一樣還會冬眠的。即使是UI層以下,底層exception,比如sql exception也不要爬到domain層里處理

           

          8. apache commons的lang包里有ExceptionUtils類,玩兒exception最好把這個工具揣口袋里。

          9.exception是設計的一部分, 但它不同于API的設計。通常我們設計API的時候,不會設計一個函數destroyBaghdad(),通常我們會寫destroyCity(Baghdad)。這樣做的目的是為了重用。換句話說,你設計API的時候,總是裝作忘了use case(caller), 而去寫適合復用的API, 盡管上面的例子use case就是destroy Baghdad, 你還是要寫更general的destroyCity函數,然后把城市的名字作為參數傳進去.但exception的設計不應該用同樣的思路做,因為你很難料想到復用的情況下,你聲明的exception是不是總能在任何情況下都得到妥善的處理。舉個例子,這是我臨時想的例子,形象但是科技太超前了。我們寫一個print()程序給打印機A,print()的時候沒有紙可以拋一個checked NoPaperException,這時候exceptino的處理程序可以自動加載紙(目前這么高級的功能正在貝爾實驗室研發呢)。也許有新型的打印機B總是先加紙,后打印,那么永遠也不存在NoPaperException。如果打印機是老式打印機C,不會自動加載紙,見到NoPaterException也無計可施,沒法處理。checked exception的哲學是,強制讓caller處理它。從上面的例子看,只有A打印機需要并能夠處理NoPaperException。 B打印機不需要處理exception。C打印機沒能力處理exception. 所以,|“需要并能夠”處理是個太嚴格的限制,一般情況下不應該用checked exception. 我們可以讓print聲明拋出unchecked exception. 提醒caller可以處理它,但是對于不應該處理它的caller也不強制去處理它。

          posted on 2011-02-22 00:12 人在江湖 閱讀(9430) 評論(5)  編輯  收藏 所屬分類: java

          Feedback

          # re: 總結java的exception 2011-02-22 21:43 人在江湖
          很奇怪,這篇blog點擊率高的離譜,你們是怎么看到這個blog的?我之前的blog每天最多一千出頭的點擊率,這篇24小時內到四千多了,真的困惑。  回復  更多評論
            

          # re: 總結java的exception 2011-02-22 23:01 HappyGao
          @人在江湖
          是從首頁看到的呀。  回復  更多評論
            

          # re: 總結java的exception[未登錄] 2011-02-24 16:29 llq
          樓主很幽默,我是初學者,多多向你學習  回復  更多評論
            

          # re: 總結java的exception 2011-02-25 13:24 Lu Han
          好文!
          最近灑家剛剛被問到回字的寫法哦!  回復  更多評論
            

          # re: 總結java的exception 2011-02-28 09:10 mashiguang
          你都罵了250了,我懂不敢告訴你是“茴”而非“回”了,管他3721,我就孔乙己一回,相信樓主明白我是好意提醒。  回復  更多評論
            

          主站蜘蛛池模板: 沙雅县| 保康县| 洛浦县| 弥渡县| 文水县| 土默特右旗| 大埔区| 商都县| 张家港市| 汝州市| 辉南县| 工布江达县| 临汾市| 台北市| 平原县| 民乐县| 政和县| 凯里市| 池州市| 桐梓县| 博乐市| 武城县| 封开县| 金昌市| 囊谦县| 涿州市| 邢台县| 洪湖市| 巴东县| 桂平市| 绵竹市| 吉隆县| 蓬安县| 安塞县| 长沙县| 沈阳市| 大姚县| 南乐县| 寿光市| 凤城市| 凤翔县|