異常

異常就是程序生病了,不處理異常,程序就會(huì)翹翹,終止運(yùn)行。對(duì)于異常的方法,一定要加上 @exception 文檔注釋。子類在重寫父類中的抽象方法時(shí)處理的異常一定要比父類中處理的異常多,也就是說(shuō)子類 throws 的異常一定只能比父類 throws 的異常少。說(shuō)的更直白點(diǎn),就是兒子一定不能比老子懶,再不濟(jì)也要一樣勤快。只要這樣世界才會(huì)不斷進(jìn)步呢!

異常通常都是交給控制層來(lái)處理的,業(yè)務(wù)層和持久層專注于功能實(shí)現(xiàn),在框架中甚至都不需要自行在控制層手工處理異常了。這是因?yàn)楫惓5脑O(shè)計(jì)之初就是為了幫助程序員方便處理錯(cuò)誤,所以對(duì)待異常的原則應(yīng)該是把出現(xiàn)問(wèn)題和處理問(wèn)題的地方分隔開來(lái)。業(yè)務(wù)層和持久層專注于處理業(yè)務(wù)和數(shù)據(jù)存儲(chǔ)的問(wèn)題,異常就全部拋給控制層去處理。

為什么要引入異常機(jī)制?

首先明確一個(gè)大前提,運(yùn)行中的程序是一定無(wú)法避免出現(xiàn)問(wèn)題的。比如斷電了或者機(jī)器硬件壞了,文件找不到或者斷網(wǎng)了。那怎么辦呢? Java 就提供了異常機(jī)制來(lái)處理這類問(wèn)題,通過(guò)異常機(jī)制來(lái)保證程序的健壯性和可維護(hù)性。

Java 提供的異常機(jī)制

問(wèn)題分為 Error 和 Exception ,比如斷電了或者機(jī)器硬件壞了,導(dǎo)致程序無(wú)法正常運(yùn)行,這類屬于 Error ,再厲害的程序員也不可能通過(guò)寫代碼解決。比如文件找不到或者斷網(wǎng)了,導(dǎo)致程序無(wú)法正常運(yùn)行,這類屬于 Exception 。Exception 又分為編譯異常和運(yùn)行異常。編譯異常通常給用戶一個(gè)良好提示,運(yùn)行異常在編寫源代碼的過(guò)程中要盡量解決掉。異常的命名應(yīng)該是能夠望文生義的,看到名字就能夠猜出異常它的作用,無(wú)論是 java 標(biāo)準(zhǔn)類庫(kù)還是自定義的異常類都應(yīng)該遵循此規(guī)則,所有東西都應(yīng)該是這樣命名的啊。比如所有的輸入輸出異常都是 IOException 的子類!

java.lang.Throwable

throwable 是一個(gè)形容詞,表示可拋出的意思。繼承于 java.lang.Object 類,是 Error 和 Exception 的直接父類,它提供了異常類的基本功能。異常類繼承層次結(jié)構(gòu)圖如下:

_Throwable.png

既然所以異常類都是繼承自 Throwable ,那么 Throwable 擁有的數(shù)據(jù)結(jié)構(gòu)對(duì)于每一個(gè)異常類都是存在的。對(duì)于異常類需要知道的數(shù)據(jù)結(jié)構(gòu)有以下幾個(gè)點(diǎn)。

  • private String detailMessage私有的 String 類型的屬性,detailMessage 屬性是用來(lái)描述異常的一段字符串信息。
  • 至少兩個(gè)構(gòu)造函數(shù),一個(gè)空構(gòu)造函數(shù),一個(gè)有參的構(gòu)造函數(shù)來(lái)給私有屬性 detailMessage 賦值。
  • public String getMessage()方法,返回的就是異常類的 detailMessage 屬性。
  • public String toString()Throwable 重寫了其父類 Object 的 toString() 方法,其輸出格式為:異常類名:detailMessage
  • public void printStackTrace()用來(lái)打印異常棧的信息,方便跟蹤程序異常信息??刂婆_(tái)中打印異常棧信息的順序是:先從異常拋出處的方法開始,一路回退到它最開始的調(diào)用方。為什么是這樣子的呢?要明白,任何知識(shí)點(diǎn)都不是孤立的,他們之間一定能夠建立聯(lián)系。實(shí)現(xiàn)這個(gè)功能其實(shí)很簡(jiǎn)單啊,每一個(gè)線程都有一個(gè)方法調(diào)用棧,所以在遇到拋出異常方法的時(shí)候,在這個(gè)方法棧里面已經(jīng)積壓了很多方法了,邊退棧邊打印調(diào)用棧中方法的信息即可。理解了這一點(diǎn),就理解了為什么會(huì)先從拋出異常方法處首先打印異常了,這不就是退棧的過(guò)程嗎?此方法還有另外兩個(gè)重載的方法,區(qū)別在于不同的參數(shù),接收不同的輸出流對(duì)象,如下public void printStackTrace(PrintWriter s)public void printStackTrace(PrintStream s)方法

編譯異常

5編譯異常中最常見的就是 IOException 了,這里也只拿這類異常舉例。編譯異常必須顯示的使用 try catch 進(jìn)行預(yù)處理,否則編譯階段就不給通過(guò)。

這其實(shí)是 java 的一種設(shè)計(jì)思想,因?yàn)槌绦蜻\(yùn)行過(guò)程中發(fā)生資源不存在問(wèn)題的可能性非常大,所以 JDK 類庫(kù)設(shè)計(jì)者提供的某些方法或者構(gòu)造方法就顯示的使用 throws 關(guān)鍵字事先聲明不處理某種異常,強(qiáng)迫調(diào)用這種方法的客戶端程序員對(duì)這類異常進(jìn)行預(yù)處理。

運(yùn)行異常

運(yùn)行異常是在程序運(yùn)行過(guò)程中遇到不正常情況,由 JVM 創(chuàng)建并拋出的異常對(duì)象,如果沒有對(duì)此異常進(jìn)行處理的話,該異常對(duì)象一路被拋到 main() 方法中,JVM 就會(huì)自動(dòng)調(diào)用該異常對(duì)象繼承子 Throwable 的public void printStackTrace()方法打印異常棧信息。這種運(yùn)行時(shí)異常是無(wú)法在編譯階段檢查出來(lái)的,因?yàn)樗耆险Z(yǔ)法規(guī)則,只有在程序運(yùn)行時(shí),JVM 才能夠判斷它是否會(huì)出現(xiàn)問(wèn)題。

比如NullPointerExceptionArithmeticException就是常見的運(yùn)行時(shí)異常類。運(yùn)行時(shí)異常才是真正讓人感覺到可怕的事情,在編寫程序的過(guò)程中,即使語(yǔ)法上不要求進(jìn)行異常處理,但是最好顯示的去判斷,去處理,程序編寫可能顯得比較麻煩,但是真正出問(wèn)題了,就會(huì)發(fā)現(xiàn)一切付出都是值得的。

異常的處理流程

這里拿運(yùn)行異常舉例,編譯異常是同理的。程序運(yùn)行過(guò)程中,JVM 發(fā)現(xiàn)不正確情況,就在 new 出一個(gè)異常對(duì)象,并給它的 detailMessage 屬性賦值。然后檢查此處是否有 try catch 捕獲了對(duì)應(yīng)的異常對(duì)象,如果有則進(jìn)入到 catch 代碼段,如果沒有則查看此方法是否使用 throws 關(guān)鍵字聲明不處理異常,如果有則到調(diào)用此方法的方法中進(jìn)行同樣的流程處理。如果都沒有,JVM 就會(huì)拋出此異常,程序被迫中斷,控制臺(tái)打印出相應(yīng)的異常信息。

捕獲異常的原則是必須要盡可能的細(xì)化,catch 代碼塊要呈金字塔鋪開。做更細(xì)致化的異常處理是為了分化問(wèn)題,便于對(duì)具體問(wèn)題做具體分析和處理。要想成為一個(gè)好的程序員,一定要做到這些。

如果在主方法中使用 throws 聲明不處理異常,這只是騙了編譯器,語(yǔ)法上是通過(guò)了,但是主方法是 JVM 調(diào)用的,相當(dāng)于還是拋給了 JVM ,該發(fā)生的異常還是會(huì)發(fā)生,程序該中斷還是會(huì)中斷。

throws 和 throw 以及 finally

throws 用在方法聲明后面,后面跟的是一個(gè)或多個(gè)異常類,表示不處理異常。throw 用在方法內(nèi)部,后面跟的是一個(gè)異常對(duì)象,表明此處拋出一個(gè)異常對(duì)象。如果一個(gè)方法中 throw 出一個(gè)異常對(duì)象,此方法就必須用 throws 聲明不處理此異常,那就會(huì)拋給調(diào)用此方法的方法去處理。或者在此方法內(nèi)部用 try catch 捕獲,否則編譯階段都不會(huì)通過(guò)。

至于 finally 關(guān)鍵字,設(shè)計(jì)之初的本意是用來(lái)關(guān)閉資源的,比如輸入輸出流發(fā)生異常,在 finally 代碼塊中關(guān)閉資源。無(wú)論程序是否發(fā)生異常,finally 的代碼都會(huì)被執(zhí)行,這是 Java 的設(shè)計(jì)機(jī)制。

try 中發(fā)生異常,如果異常被 catch 捕獲,則先執(zhí)行catch 的語(yǔ)句,再執(zhí)行 finally 的語(yǔ)句。如果異常沒有被捕獲,會(huì)先執(zhí)行 finally 中的代碼,再拋出此異常,因?yàn)槿绻葤伋隽水惓?,?finally 代碼塊的內(nèi)容就無(wú)法執(zhí)行了。

finally 簡(jiǎn)直太牛了,即使它前面有 return 語(yǔ)句,也會(huì)等到 fianlly 代碼塊的語(yǔ)句執(zhí)行完了后再返回,但是如果 finally 有 return 語(yǔ)句,就會(huì)覆蓋之前的語(yǔ)句,可以利用 return 返回棧的概念分析。實(shí)例代碼如下:

public static int f() {
        try {
            System.out.println("try"+ 5/0);
            return 1;
        }catch(Exception e){
            return 3;
        }finally {
            return 2;
        }
    }//此方法返回 2 

如果 catch 捕獲了相應(yīng)異常,但是在處理異常的過(guò)程中又發(fā)生了異常,那么此時(shí)本應(yīng)該拋出異常,但是會(huì)先執(zhí)行 finally 代碼塊的內(nèi)容后再拋出異常。但是如果 finally 代碼塊中有 return 語(yǔ)句,不僅會(huì)覆蓋之前所有的 return 語(yǔ)句,還會(huì)是的程序就此結(jié)束。之前未來(lái)得及處理的異常就這樣被隱藏了。

finally 代碼塊中不要出現(xiàn) return 語(yǔ)句,不要亂寫。上面講到的內(nèi)容只是有可能會(huì)遇到這樣的面試題,只要知道 finally 中的 return 語(yǔ)句有一個(gè) return 返回棧的概念就可以了,利用這個(gè)概念輔助分析。

自定義異常類

為什么要使用自定義異常類?Java 異常機(jī)制就是設(shè)計(jì)來(lái)處理程序中不正確問(wèn)題的一種手段。這些都屬于程序運(yùn)行上的問(wèn)題,計(jì)算機(jī)是用來(lái)解決問(wèn)題的,在現(xiàn)實(shí)中有很多功能性錯(cuò)誤,也就是程序運(yùn)行上沒有問(wèn)題,但是在具體業(yè)務(wù)功能上不符合需求。比如銀行系統(tǒng)中如果用戶取錢大于賬戶余額,這就屬于業(yè)務(wù)功能性錯(cuò)誤。這類問(wèn)題可以巧妙的利用異常機(jī)制來(lái)完成對(duì)她的處理。

如何自定義異常?自定義異常首先要做的當(dāng)然是繼承 java.lang.Exception 類,不然你要自己重新寫一個(gè)嗎?然后提供一個(gè)空構(gòu)造函數(shù)和一個(gè)有參的構(gòu)造函數(shù),在其內(nèi)部調(diào)用 super 關(guān)鍵字給父類的私有屬性 detailMessage 屬性賦值。重寫 toString() 方法,輸出格式為:類名:detailMessage。根據(jù)具體需求還可以重寫printStackTrace()方法。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 引言 在程序運(yùn)行過(guò)程中(注意是運(yùn)行階段,程序可以通過(guò)編譯),如果JVM檢測(cè)出一個(gè)不可能執(zhí)行的操作,就會(huì)出現(xiàn)運(yùn)行時(shí)錯(cuò)...
    Steven1997閱讀 2,596評(píng)論 1 6
  • packagetestexcrpltiom; importjava.text.ParseException; im...
    猿學(xué)閱讀 1,528評(píng)論 0 2
  • 這一日,我們幾經(jīng)輾轉(zhuǎn),來(lái)到貴州最有名的古鎮(zhèn)故鄉(xiāng)——鎮(zhèn)遠(yuǎn)。 鎮(zhèn)遠(yuǎn)是座歷史悠久的苗鄉(xiāng)古鎮(zhèn),苗族、侗族自治州...
    淡定姐姐閱讀 357評(píng)論 0 1
  • 現(xiàn)在的男女認(rèn)識(shí)真的 好簡(jiǎn)單,陌陌?探探?附近的人?等等各種交友平臺(tái),往往這樣的感情真的不會(huì)有人去珍惜吧畢竟來(lái)的太快...
    過(guò)呀過(guò)呀閱讀 305評(píng)論 0 0
  • 我知道這是今年最后一天和你在一起 你才哭的這么傷心 哭了一整天 整個(gè)江南因?yàn)槟阋叨伎蘖?秋天 我的情人 沒有人知...
    王秋煜閱讀 313評(píng)論 6 5

友情鏈接更多精彩內(nèi)容