<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    自由飛翔

    我在仰望,java之上

    統(tǒng)計(jì)

    留言簿(2)

    我關(guān)注的blog

    閱讀排行榜

    評(píng)論排行榜

    轉(zhuǎn)載:JAVA異常機(jī)制介紹/如何正確的進(jìn)行JAVA異常處理

     文章來源:
     作者:Maverick
     blog:http://blog.csdn.net/zhaohuabing  

    1  引言
    在JAVA語言出現(xiàn)以前,傳統(tǒng)的異常處理方式多采用返回值來標(biāo)識(shí)程序出現(xiàn)的異常情況,這種方式雖然為程序員所熟悉,但卻有多個(gè)壞處。首先,一個(gè)API可以返回任意的返回值,而這些返回值本身并不能解釋該返回值是否代表一個(gè)異常情況發(fā)生了和該異常的具體情況,需要調(diào)用API的程序自己判斷并解釋返回值的含義。其次,并沒有一種機(jī)制來保證異常情況一定會(huì)得到處理,調(diào)用程序可以簡單的忽略該返回值,需要調(diào)用API的程序員記住去檢測返回值并處理異常情況。這種方式還讓程序代碼變得晦澀冗長,當(dāng)進(jìn)行IO操作等容易出現(xiàn)異常情況的處理時(shí),你會(huì)發(fā)現(xiàn)代碼的很大部分用于處理異常情況的switch分支,程序代碼的可讀性變得很差。
    上面提到的問題,JAVA的異常處理機(jī)制提供了很好的解決方案。通過拋出JDK預(yù)定義或者自定義的異常,能夠表明程序中出現(xiàn)了什么樣的異常情況;而且JAVA的語言機(jī)制保證了異常一定會(huì)得到恰當(dāng)?shù)奶幚恚缓侠淼氖褂卯惓L幚頇C(jī)制,會(huì)讓程序代碼清晰易懂。
    2 JAVA異常的處理機(jī)制
        當(dāng)程序中拋出一個(gè)異常后,程序從程序中導(dǎo)致異常的代碼處跳出,java虛擬機(jī)檢測尋找和try關(guān)鍵字匹配的處理該異常的catch塊,如果找到,將控制權(quán)交到catch塊中的代碼,然后繼續(xù)往下執(zhí)行程序,try塊中發(fā)生異常的代碼不會(huì)被重新執(zhí)行。如果沒有找到處理該異常的catch塊,在所有的finally塊代碼被執(zhí)行和當(dāng)前線程的所屬的ThreadGroup的uncaughtException方法被調(diào)用后,遇到異常的當(dāng)前線程被中止。
    3 JAVA異常的類層次
    JAVA異常的類層次如下圖所示:

    圖1 JAVA異常的類層次
    Throwable是所有異常的基類,程序中一般不會(huì)直接拋出Throwable對(duì)象,Exception和Error是Throwable的子類,Exception下面又有RuntimeException和一般的Exception兩類。可以把JAVA異常分為三類:
            第一類是Error,Error表示程序在運(yùn)行期間出現(xiàn)了十分嚴(yán)重、不可恢復(fù)的錯(cuò)誤,在這種情況下應(yīng)用程序只能中止運(yùn)行,例如JAVA 虛擬機(jī)出現(xiàn)錯(cuò)誤。Error是一種unchecked Exception,編譯器不會(huì)檢查Error是否被處理,在程序中不用捕獲Error類型的異常;一般情況下,在程序中也不應(yīng)該拋出Error類型的異常。
            第二類是RuntimeException, RuntimeException 是一種unchecked Exception,即表示編譯器不會(huì)檢查程序是否對(duì)RuntimeException作了處理,在程序中不必捕獲RuntimException類型的異常,也不必在方法體聲明拋出RuntimeException類。RuntimeException發(fā)生的時(shí)候,表示程序中出現(xiàn)了編程錯(cuò)誤,所以應(yīng)該找出錯(cuò)誤修改程序,而不是去捕獲RuntimeException。
            第三類是一般的checked Exception,這也是在編程中使用最多的Exception,所有繼承自Exception并且不是RuntimeException的異常都是checked Exception,如圖1中的IOException和ClassNotFoundException。JAVA 語言規(guī)定必須對(duì)checked Exception作處理,編譯器會(huì)對(duì)此作檢查,要么在方法體中聲明拋出checked Exception,要么使用catch語句捕獲checked Exception進(jìn)行處理,不然不能通過編譯。checked Exception用于以下的語義環(huán)境:

    (1) 該異常發(fā)生后是可以被恢復(fù)的,如一個(gè)Internet連接發(fā)生異常被中止后,可以重新連接再進(jìn)行后續(xù)操作。
    (2) 程序依賴于不可靠的外部條件,該依賴條件可能出錯(cuò),如系統(tǒng)IO。
    (3) 該異常發(fā)生后并不會(huì)導(dǎo)致程序處理錯(cuò)誤,進(jìn)行一些處理后可以繼續(xù)后續(xù)操作。

    4 JAVA異常處理中的注意事項(xiàng)
    合理使用JAVA異常機(jī)制可以使程序健壯而清晰,但不幸的是,JAVA異常處理機(jī)制常常被錯(cuò)誤的使用,下面就是一些關(guān)于Exception的注意事項(xiàng):

    1. 不要忽略checked Exception
    請(qǐng)看下面的代碼:
    try
    {
      method1();  //method1拋出ExceptionA
    }
    catch(ExceptionA e)
    {
        e.printStackTrace();
    }
    上面的代碼似乎沒有什么問題,捕獲異常后將異常打印,然后繼續(xù)執(zhí)行。事實(shí)上在catch塊中對(duì)發(fā)生的異常情況并沒有作任何處理(打印異常不能是算是處理異常,因?yàn)樵诔绦蚪桓哆\(yùn)行后調(diào)試信息就沒有什么用處了)。這樣程序雖然能夠繼續(xù)執(zhí)行,但是由于這里的操作已經(jīng)發(fā)生異常,將會(huì)導(dǎo)致以后的操作并不能按照預(yù)期的情況發(fā)展下去,可能導(dǎo)致兩個(gè)結(jié)果:
    一是由于這里的異常導(dǎo)致在程序中別的地方拋出一個(gè)異常,這種情況會(huì)使程序員在調(diào)試時(shí)感到迷惑,因?yàn)樾碌漠惓伋龅牡胤讲⒉皇浅绦蛘嬲l(fā)生問題的地方,也不是發(fā)生問題的真正原因;
    另外一個(gè)是程序繼續(xù)運(yùn)行,并得出一個(gè)錯(cuò)誤的輸出結(jié)果,這種問題更加難以捕捉,因?yàn)楹芸赡馨阉?dāng)成一個(gè)正確的輸出。
    那么應(yīng)該如何處理呢,這里有四個(gè)選擇:

    (1) 處理異常,進(jìn)行修復(fù)以讓程序繼續(xù)執(zhí)行。
    (2) 重新拋出異常,在對(duì)異常進(jìn)行分析后發(fā)現(xiàn)這里不能處理它,那么重新拋出異常,讓調(diào)用者處理。
    (3) 將異常轉(zhuǎn)換為用戶可以理解的自定義異常再拋出,這時(shí)應(yīng)該注意不要丟失原始異常信息(見5)。
    (4) 不要捕獲異常。

    因此,當(dāng)捕獲一個(gè)unchecked Exception的時(shí)候,必須對(duì)異常進(jìn)行處理;如果認(rèn)為不必要在這里作處理,就不要捕獲該異常,在方法體中聲明方法拋出異常,由上層調(diào)用者來處理該異常。

    2. 不要一次捕獲所有的異常
    請(qǐng)看下面的代碼:
    try
    {
      method1();  //method1拋出ExceptionA
        method2();  //method1拋出ExceptionB
        method3();  //method1拋出ExceptionC
    }
    catch(Exception e)
    {
        ……
    }
    這是一個(gè)很誘人的方案,代碼中使用一個(gè)catch子句捕獲了所有異常,看上去完美而且簡潔,事實(shí)上很多代碼也是這樣寫的。但這里有兩個(gè)潛在的缺陷,一是針對(duì)try塊中拋出的每種Exception,很可能需要不同的處理和恢復(fù)措施,而由于這里只有一個(gè)catch塊,分別處理就不能實(shí)現(xiàn)。二是try塊中還可能拋出RuntimeException,代碼中捕獲了所有可能拋出的RuntimeException而沒有作任何處理,掩蓋了編程的錯(cuò)誤,會(huì)導(dǎo)致程序難以調(diào)試。
    下面是改正后的正確代碼:
    try
    {
      method1();  //method1拋出ExceptionA
        method2();  //method1拋出ExceptionB
        method3();  //method1拋出ExceptionC
    }
    catch(ExceptionA e)
    {
        ……
    }
    catch(ExceptionB e)
    {
        ……
    }
    catch(ExceptionC e)
    {
        ……
    }


    3. 使用finally塊釋放資源
        finally關(guān)鍵字保證無論程序使用任何方式離開try塊,finally中的語句都會(huì)被執(zhí)行。在以下三種情況下會(huì)進(jìn)入finally塊:
    (1) try塊中的代碼正常執(zhí)行完畢。
    (2) 在try塊中拋出異常。
    (3) 在try塊中執(zhí)行return、break、continue。
    因此,當(dāng)你需要一個(gè)地方來執(zhí)行在任何情況下都必須執(zhí)行的代碼時(shí),就可以將這些
    代碼放入finally塊中。當(dāng)你的程序中使用了外界資源,如數(shù)據(jù)庫連接,文件等,必須將釋放這些資源的代碼寫入finally塊中。
    必須注意的是,在finally塊中不能拋出異常。JAVA異常處理機(jī)制保證無論在任何情況下必須先執(zhí)行finally塊然后在離開try塊,因此在try塊中發(fā)生異常的時(shí)候,JAVA虛擬機(jī)先轉(zhuǎn)到finally塊執(zhí)行finally塊中的代碼,finally塊執(zhí)行完畢后,再向外拋出異常。如果在finally塊中拋出異常,try塊捕捉的異常就不能拋出,外部捕捉到的異常就是finally塊中的異常信息,而try塊中發(fā)生的真正的異常堆棧信息則丟失了。
    請(qǐng)看下面的代碼:

    Connection  con = null;
    try
    {
        con = dataSource.getConnection();
        ……
    }
    catch(SQLException e)
    {
        ……
        throw e;//進(jìn)行一些處理后再將數(shù)據(jù)庫異常拋出給調(diào)用者處理
    }
    finally
    {
        try
        {
            con.close();
        }
        catch(SQLException e)
    {
        e.printStackTrace();
        ……
    }
    }
    運(yùn)行程序后,調(diào)用者得到的信息如下
    java.lang.NullPointerException
     at myPackage.MyClass.method1(methodl.java:266)
    而不是我們期望得到的數(shù)據(jù)庫異常。這是因?yàn)檫@里的con是null的關(guān)系,在finally語句中拋出了NullPointerException,在finally塊中增加對(duì)con是否為null的判斷可以避免產(chǎn)生這種情況。

    4. 異常不能影響對(duì)象的狀態(tài)
    異常產(chǎn)生后不能影響對(duì)象的狀態(tài),這是異常處理中的一條重要規(guī)則。 在一個(gè)函數(shù)
    中發(fā)生異常后,對(duì)象的狀態(tài)應(yīng)該和調(diào)用這個(gè)函數(shù)之前保持一致,以確保對(duì)象處于正確的狀態(tài)中。
    如果對(duì)象是不可變對(duì)象(不可變對(duì)象指調(diào)用構(gòu)造函數(shù)創(chuàng)建后就不能改變的對(duì)象,即
        創(chuàng)建后沒有任何方法可以改變對(duì)象的狀態(tài)),那么異常發(fā)生后對(duì)象狀態(tài)肯定不會(huì)改變。如果是可變對(duì)象,必須在編程中注意保證異常不會(huì)影響對(duì)象狀態(tài)。有三個(gè)方法可以達(dá)到這個(gè)目的:
    (1) 將可能產(chǎn)生異常的代碼和改變對(duì)象狀態(tài)的代碼分開,先執(zhí)行可能產(chǎn)生異常的代碼,如果產(chǎn)生異常,就不執(zhí)行改變對(duì)象狀態(tài)的代碼。
    (2) 對(duì)不容易分離產(chǎn)生異常代碼和改變對(duì)象狀態(tài)代碼的方法,定義一個(gè)recover方法,在異常產(chǎn)生后調(diào)用recover方法修復(fù)被改變的類變量,恢復(fù)方法調(diào)用前的類狀態(tài)。
    (3) 在方法中使用對(duì)象的拷貝,這樣當(dāng)異常發(fā)生后,被影響的只是拷貝,對(duì)象本身不會(huì)受到影響。

    5. 丟失的異常
    請(qǐng)看下面的代碼:
    public void method2()
    {
    try
    {
        ……
        method1();  //method1進(jìn)行了數(shù)據(jù)庫操作
    }
    catch(SQLException e)
    {
        ……
        throw new MyException(“發(fā)生了數(shù)據(jù)庫異常:”+e.getMessage);
    }
    }
    public void method3()
    {
        try
    {
        method2();
    }
    catch(MyException e)
    {
        e.printStackTrace();
        ……
    }
    }
    上面method2的代碼中,try塊捕獲method1拋出的數(shù)據(jù)庫異常SQLException后,拋出了新的自定義異常MyException。這段代碼是否并沒有什么問題,但看一下控制臺(tái)的輸出:
    MyException:發(fā)生了數(shù)據(jù)庫異常:對(duì)象名稱 'MyTable' 無效。
    at MyClass.method2(MyClass.java:232)
    at MyClass.method3(MyClass.java:255)
    原始異常SQLException的信息丟失了,這里只能看到method2里面定義的MyException的堆棧情況;而method1中發(fā)生的數(shù)據(jù)庫異常的堆棧則看不到,如何排錯(cuò)呢,只有在method1的代碼行中一行行去尋找數(shù)據(jù)庫操作語句了,祈禱method1的方法體短一些吧。
    JDK的開發(fā)者們也意識(shí)到了這個(gè)情況,在JDK1.4.1中,Throwable類增加了兩個(gè)構(gòu)造方法,public Throwable(Throwable cause)和public Throwable(String message,Throwable cause),在構(gòu)造函數(shù)中傳入的原始異常堆棧信息將會(huì)在printStackTrace方法中打印出來。但對(duì)于還在使用JDK1.3的程序員,就只能自己實(shí)現(xiàn)打印原始異常堆棧信息的功能了。實(shí)現(xiàn)過程也很簡單,只需要在自定義的異常類中增加一個(gè)原始異常字段,在構(gòu)造函數(shù)中傳入原始異常,然后重載printStackTrace方法,首先調(diào)用類中保存的原始異常的printStackTrace方法,然后再調(diào)用super.printStackTrace方法就可以打印出原始異常信息了。可以這樣定義前面代碼中出現(xiàn)的MyException類:
    public class MyExceptionextends Exception
    {
        //構(gòu)造函數(shù)
        public SMException(Throwable cause)
        {
            this.cause_ = cause;
        }

        public MyException(String s,Throwable cause)
        {
            super(s);
            this.cause_ = cause;
        }
        //重載printStackTrace方法,打印出原始異常堆棧信息
        public void printStackTrace()
        {
            if (cause_ != null)
            {
                cause_.printStackTrace();
            }
            super.printStackTrace(s);
        }

        public void printStackTrace(PrintStream s)
        {
            if (cause_ != null)
            {
                cause_.printStackTrace(s);
            }
            super.printStackTrace(s);
        }

        public void printStackTrace(PrintWriter s)
        {
            if (cause_ != null)
            {
                cause_.printStackTrace(s);
            }
            super.printStackTrace(s);
        }
         //原始異常
         private Throwable cause_;
    }

    6. 不要使用同時(shí)使用異常機(jī)制和返回值來進(jìn)行異常處理
    下面是我們項(xiàng)目中的一段代碼
    try
    {
        doSomething(); 
    }
    catch(MyException e)
    {
    if(e.getErrcode == -1)
    {
        ……
    }
    if(e.getErrcode == -2)
    {
       ……
    }
    ……
    }
    假如在過一段時(shí)間后來看這段代碼,你能弄明白是什么意思嗎?混合使用JAVA異常處理機(jī)制和返回值使程序的異常處理部分變得“丑陋不堪”,并難以理解。如果有多種不同的異常情況,就定義多種不同的異常,而不要像上面代碼那樣綜合使用Exception和返回值。
    修改后的正確代碼如下:
    try
    {
        doSomething();  //拋出MyExceptionA和MyExceptionB
    }
    catch(MyExceptionA e)
    {
    ……
    }
    catch(MyExceptionB e)
    {
        ……
    }


    7. 不要讓try塊過于龐大
    出于省事的目的,很多人習(xí)慣于用一個(gè)龐大的try塊包含所有可能產(chǎn)生異常的代碼,
    這樣有兩個(gè)壞處:
    閱讀代碼的時(shí)候,在try塊冗長的代碼中,不容易知道到底是哪些代碼會(huì)拋出哪些異常,不利于代碼維護(hù)。
    使用try捕獲異常是以程序執(zhí)行效率為代價(jià)的,將不需要捕獲異常的代碼包含在try塊中,影響了代碼執(zhí)行的效率。



    Gavin

    posted on 2011-09-30 01:08 GavinMiao 閱讀(353) 評(píng)論(0)  編輯  收藏 所屬分類: corejava

    主站蜘蛛池模板: 久久精品国产亚洲av品善| 免费理论片51人人看电影| 成年人视频免费在线观看| 日韩免费高清一级毛片| 91大神亚洲影视在线| 成人影片麻豆国产影片免费观看| 久久国产精品免费一区二区三区| 亚洲日韩乱码中文字幕| 亚洲精品视频专区| 国产成人A亚洲精V品无码| 国产免费131美女视频| 国产麻豆视频免费观看| 8x8×在线永久免费视频| 99久久免费国产精品热| 成人午夜免费视频| 最好2018中文免费视频| 在线精品自拍亚洲第一区| 亚洲最大的成人网站| 亚洲冬月枫中文字幕在线看| 色婷婷亚洲十月十月色天| 亚洲VA中文字幕无码毛片| 亚洲一区二区三区偷拍女厕| yy6080久久亚洲精品| 国产成人无码免费视频97| 午夜影视在线免费观看| 成人影片麻豆国产影片免费观看 | 美女黄网站人色视频免费国产| 18禁黄网站禁片免费观看不卡| 麻豆精品不卡国产免费看| a级毛片免费在线观看| 久久久久久噜噜精品免费直播| 免费国产在线精品一区| 免费无码午夜福利片| 男女作爱免费网站| 人成免费在线视频| eeuss免费影院| 成在人线av无码免费高潮水| 久久久久久久国产免费看| a色毛片免费视频| 在线观看免费播放av片| 久久国产精品2020免费m3u8|