thinking

          one platform thousands thinking

          如何避免在Java中使用Checked Exception

          如何避免在Java中使用Checked Exception


          發(fā)布時(shí)間:2007.08.20 08:52     來(lái)源:賽迪網(wǎng)    作者:dxaw

          這 篇文章指出了Java中checked Exception的一些缺點(diǎn),提出應(yīng)該在程序設(shè)計(jì)中避免使用checked Exception,對(duì)于需要處理checked Exception的代碼,可以使用ExceptionAdapter這個(gè)類對(duì)checked Exception進(jìn)行包裝。這篇文章的概念和ExceptionAdapter這個(gè)類均源自Bruce Eckel的Does Java need Checked Exception。

          Java的Exception分為兩類,一類是RuntimeException及 其子類,另外一類就是checked Exception。Java要求函數(shù)對(duì)沒有被catch處理掉的checked Exception,需要將其寫在函數(shù)的聲明部分。然而,這一要求常常給程序員帶來(lái)一些不必要的負(fù)擔(dān)。

          為了避免在函數(shù)聲明中寫throws部分,在Java項(xiàng)目里面常常可以看到以下代碼用來(lái)‘吞掉’Exception:

          try {



          // ...



          } catch (Exception ex) {



          ex.printStackTrace();



          }

          這顯然不是一個(gè)好的處理Exception辦法,事實(shí)上,catch并處理一個(gè)Exception意味著讓程序從發(fā)生的錯(cuò)誤(Exception)中恢復(fù)過(guò)來(lái)。從這種意義上說(shuō),已上的代碼只可能在一些很簡(jiǎn)單的情況下工作而不帶來(lái)問(wèn)題。

          對(duì)于很多Exception,往往沒有去處理它并讓程序從錯(cuò)誤中恢復(fù)出來(lái)的辦法,這 時(shí)唯一能做的事情可能就是在界面上顯示一些提示信息給用戶。這種情況下讓程序拋出遇到的Exception是更為合理的做法。然而,這樣做會(huì)使得一些函數(shù) 的聲明急劇膨脹。一個(gè)函數(shù)可能需要聲明會(huì)拋出的7、8個(gè)checked Exception,而且每個(gè)調(diào)用它的函數(shù)也需要同樣的聲明。

          比這更糟糕的是,這有可能破壞類設(shè)計(jì)的open-close原則。簡(jiǎn)單來(lái) 說(shuō),open-close原則是指當(dāng)擴(kuò)展一個(gè)模塊的時(shí)候,可以不影響其現(xiàn)有的client。open-close原則是通過(guò)繼承來(lái)實(shí)現(xiàn)的,當(dāng)繼承一個(gè)類的 時(shí)候,我們既擴(kuò)展了這個(gè)類,也不會(huì)影響原有的client(因?yàn)閷?duì)這個(gè)類沒有改動(dòng))。

          現(xiàn)在考慮下面這種情況,有一個(gè)父類Base:

          public class Base {

          public void foo() throws ExceptionA {

          // ...

          }

          }

          現(xiàn)在需要繼承Base這個(gè)類并重載foo這個(gè)方法,在新的實(shí)現(xiàn)中,foo可能拋出ExceptionB:

          public class Extend extends Base {

          public void foo() throws ExceptionB {

          // ...

          }

          }

          然而,這樣寫在Java里面是不合法的,因?yàn)镴ava把可能會(huì)拋出的Exception看作函數(shù)特征的一部分,子類聲明拋出的Exception必須是父類的子集。

          可以在Base類的foo方法中加入拋出ExceptionB的聲明,然而,這樣就破壞了open-close原則。而且,有時(shí)我們沒有辦法去修改父類,比如當(dāng)重載一個(gè)Jdk里的類的時(shí)候。

          另一個(gè)可能的做法是在Extend的foo方法中catch住ExceptionB,然后構(gòu)造一個(gè)ExceptionA并拋出。這是個(gè)可行的辦法但也只是一個(gè)權(quán)宜之計(jì)。

          如果使用RuntimeException,這些問(wèn)題都不會(huì)存在。這說(shuō)明 checked Exception并不是一個(gè)很實(shí)用的概念,也意味著在程序設(shè)計(jì)的時(shí)候,我們應(yīng)該讓自己的Exception類繼承RuntimeException而不 是Exception。(這和JDK的建議正好相反,但實(shí)踐證明這樣做代碼的質(zhì)量更好。)

          對(duì)于那些需要處理checked Exception的代碼,可以利用一個(gè)ExceptionAdapter的類把checked Exception包裝成一個(gè)RuntimeException拋出。ExceptionAdapter來(lái)自Bruce Eckel的Does Java need Checked Exception這篇文章,在這里的ExceptionAdapter是我根據(jù)JDK 1.4修改過(guò)的:

          public class ExceptionAdapter extends RuntimeException {



          public ExceptionAdapter(Exception ex) {



          super(ex);

          }



          public void printStackTrace(java.io.PrintStream s) {



          getCause().printStackTrace(s);

          }



          public void printStackTrace(java.io.PrintWriter s) {



          getCause().printStackTrace(s);



          }



          // rethrow()的作用是把被包裝的Exception再次拋出。



          public void rethrow()

          throws Exception

          {

          throw (Exception) getCause();

          }



          }

          (責(zé)任編輯:編輯龔勛)

          posted on 2009-02-17 11:18 lau 閱讀(301) 評(píng)論(1)  編輯  收藏 所屬分類: J2SE

          Feedback

          # re: 如何避免在Java中使用Checked Exception 2010-11-15 18:43 lau

          Java里有個(gè)很重要的特色是Exception ,也就是說(shuō)允許程序產(chǎn)生例外狀況。而在學(xué)Java 的時(shí)候,我們也只知道Exception 的寫法,卻未必真能了解不同種類的Exception 的區(qū)別。

            首先,您應(yīng)該知道的是Java 提供了兩種Exception 的模式,一種是執(zhí)行的時(shí)候所產(chǎn)生的Exception (Runtime Exception),另外一種則是受控制的Exception (Checked Exception)。

            所有的Checked Exception 均從java.lang.Exception 繼承而來(lái),而Runtime Exception 則繼承java.lang.RuntimeException 或java.lang.Error (實(shí)際上java.lang.RuntimeException 的上一層也是java.lang.Exception)。

            當(dāng)我們撰寫程序的時(shí)候,我們很可能會(huì)對(duì)選擇某種形式的Exception 感到困擾,到底我應(yīng)該選擇Runtime Exception 還是Checked Exception ?

            其實(shí),在運(yùn)作上,我們可以通過(guò)Class 的Method 如何產(chǎn)生某個(gè)Exception以及某個(gè)程序如何處理這個(gè)被產(chǎn)生來(lái)的Exception 來(lái)了解它們之間的差異。
          首先我們先建立一個(gè)Exception

          public class CException extends Exception
          {
          public CException() {}
          public CException(String message)
          {
          super(message);
          }
          }

          然后我們撰寫一個(gè)可能產(chǎn)生 CException 的 Class

          public class testException
          {
          public void method1() throws CException
          {
          throw new CException("Test Exception");
          }

          public void method2(String msg)
          {
          if(msg == null)
          {
          throw new NullPointerException("Message is null");
          }
          }

          public void method3() throws CException
          {
          method1();
          }

          // 以下省略
          // ...
          }

            在這三個(gè)method 中,我們看到了method1 和method2 的程序碼內(nèi)都會(huì)產(chǎn)生Exception,但method3 的程序碼中(大括號(hào)內(nèi)),并沒產(chǎn)生Exception,但在method3 的定義中,暗示了這個(gè)method 可能產(chǎn)生CException。

          呼叫method1() 的程序,必須將method1() 包含在try 與catch 中,如:

          public class runtest
          {
          // ....
          public static void main(String argv[])
          {
          testException te = new testException();
          try
          {
          te.method1();
          }
          catch(CException ce)
          {
          // ....
          }
          }
          // ...
          }

            雖然包含在try 與catch 中,并不表示這段程序碼一定會(huì)收到CException,但它的用意在于提醒呼叫者,執(zhí)行這個(gè)method 可能產(chǎn)生的意外,而使用者也必須要能針對(duì)這個(gè)意外做出相對(duì)應(yīng)的處理方式。

            當(dāng)使用者呼叫method2() 時(shí),并不需要使用try 和catch 將程序碼包起來(lái),因?yàn)閙ethod2 的定義中,并沒有throws 任何的Exception ,如:

          public class runtest
          {
          // ....
          public static void main(String argv[])
          {

          testException te = new testException();

          // 不會(huì)產(chǎn)生 Exception
          te.method2("Hello");

          // 會(huì)產(chǎn)生 Exception
          te.method2(null);
          }
          // ...
          }

            程序在執(zhí)行的時(shí)候,也不見得會(huì)真的產(chǎn)生NullPointerException ,這種Exception 叫做runtime exception 也有人稱為unchecked exception ,產(chǎn)生Runtime Exception 的method (在這個(gè)范例中是method2) 并不需要在宣告method 的時(shí)候定義它將會(huì)產(chǎn)生哪一種Exception 。

          在testException 的method3() 中,我們看到了另外一種狀況,也就是method3里呼叫了method1() ,但卻沒有將method1 包在try 和catch 之間。相反,在method3() 的定義中,它定義了CException,實(shí)際上就是如果method3 收到了CException ,它將不處理這個(gè)CException ,而將它往外丟。當(dāng)然,由于method3 的定義中有throws CException ,因此呼叫method3 的程序碼也需要有try catch 才行。

            因此從程序的運(yùn)作機(jī)制上看,Runtime Exception與Checked Exception 不一樣,然而從邏輯上看,Runtime Exception 與Checked Exception 在使用的目的上也不一樣。

            一般而言,Checked Exception 表示這個(gè)Exception 必須要被處理,也就是說(shuō)程序設(shè)計(jì)者應(yīng)該已經(jīng)知道可能會(huì)收到某個(gè)Exception(因?yàn)橐猼ry catch住) ,所以程序設(shè)計(jì)者應(yīng)該能針對(duì)這些不同的Checked Exception 做出不同的處理。

            而Runtime Exception 通常會(huì)暗示著程序上的錯(cuò)誤,這種錯(cuò)誤會(huì)導(dǎo)致程序設(shè)計(jì)者無(wú)法處理,而造成程序無(wú)法繼續(xù)執(zhí)行下去。

          看看下面的例子:

          String message[] = {"message1", "message2","message3"};
          System.out.println(message[3]);

            這段程序碼在Compile 時(shí)并沒問(wèn)題,但在執(zhí)行時(shí)則會(huì)出現(xiàn)ArrayIndexOutOfBoundException 的例外,在這種狀況下,我們亦無(wú)法針對(duì)這個(gè)Runtime Exception 做出有意義的動(dòng)作,這就像是我們呼叫了testException 中的method2 ,卻引發(fā)了它的NullPointerException 一樣,在這種狀況下,我們必須對(duì)程序碼進(jìn)行修改,從而避免這個(gè)問(wèn)題。

            因此,實(shí)際上我們應(yīng)該也必須要去抓取所有的Checked Exception,同時(shí)最好能在這些Checked Exception 發(fā)生的時(shí)候做出相對(duì)應(yīng)的處理,好讓程序能面對(duì)不同的狀況。

          然而對(duì)于Runtime Exception ,有些人建議將它c(diǎn)atch 住,然后導(dǎo)向其它地方,讓程序繼續(xù)執(zhí)行下去,這種作法并非不好,但它會(huì)讓我們?cè)谀承y(cè)試工具下認(rèn)為我們的程序碼沒有問(wèn)題,因?yàn)槲覀儗untime Exception "處理"掉了,事實(shí)卻不然!譬如很多人的習(xí)慣是在程序的進(jìn)入點(diǎn)后用個(gè)大大的try catch 包起來(lái),如:

          public class runtest1
          {
          public static void main(String argv[])
          {
          try
          {
          //...
          }
          catch(Exception e)
          {
          }
          }
          }

            在這種情況下,我們很可能會(huì)不知道發(fā)生了什么Exception 或是從哪一行發(fā)出的,因此在面對(duì)不同的Checked Exception時(shí),我們可已分別去try catch它。而在測(cè)試階段時(shí),如果碰到Runtime Exception ,我們可以讓它就這樣發(fā)生,接著再去修改我們的程序碼,讓它避免Runtime Exception,否則,我們就應(yīng)該仔細(xì)追究每一個(gè)Exception ,直到我們可以確定它不會(huì)有Runtime Exception 為止!

            對(duì)于Checked Exception 與Runtime Exception ,我想應(yīng)該有不少人會(huì)有不同的觀點(diǎn),無(wú)論如何,程序先要能執(zhí)行,這些Exception 才有機(jī)會(huì)產(chǎn)生。因此,我們可以把這些Exception 當(dāng)成是Bug ,也可以當(dāng)成是不同的狀況(Checked Exception),或當(dāng)成是幫助我們除錯(cuò)的工具(Runtime Exception),但前提是我們需要處理這些Exception ,如果不處理,那么問(wèn)題或狀況就會(huì)永遠(yuǎn)留在那里。  回復(fù)  更多評(píng)論   

          主站蜘蛛池模板: 吉安市| 富宁县| 峨山| 枣强县| 兴业县| 贡山| 嘉峪关市| 镇安县| 静宁县| 称多县| 衡阳县| 邻水| 巴彦县| 吴忠市| 文山县| 磐安县| 临泽县| 垦利县| 青龙| 新建县| 榆中县| 山阴县| 井冈山市| 丹东市| 霍山县| 高青县| 麻栗坡县| 沁源县| 建湖县| 鹤峰县| 邵东县| 罗江县| 泾阳县| 玉屏| 隆德县| 西吉县| 华坪县| 建始县| 崇明县| 图们市| 星座|