吃透Java異常體系

Java的異常都是派生于Throwable類的一個實例敌厘,所有的異常都是由Throwable繼承而來的滴须。Throwable有分為了Error類和Exception類舌狗。

Error(錯誤)

Error 類層次結(jié)構(gòu)描述了 Java 運行時系統(tǒng)的內(nèi)部錯誤和資源耗盡錯誤。Error表示比較嚴重的問題扔水,一般是JVM運行時出現(xiàn)了錯誤痛侍,如沒有內(nèi)存可分配拋出OOM錯誤、棧資源耗盡拋出StackOverflowError錯誤魔市、Java虛擬機運行錯誤Virtual MachineError主届、類定義錯誤NoClassDefFoundError。如果出現(xiàn)了這樣的內(nèi)部錯誤待德, 除了通告給用戶君丁,并盡力使程序安全地終止之外, 再也無能為力了将宪。

Exception(異常)

異常又分為RuntimeException和其他異常绘闷。由程序錯誤導致的異常屬于RuntimeException橡庞,而程序本身沒有問題,但由于像 I/O 錯誤這類問題導致的異常屬于其他異常印蔗。

運行時異常RuntimeException:顧名思義扒最,運行時才可能拋出的異常,編譯器不會處理此類異常华嘹。比如數(shù)組索引越界吧趣、使用的對象為空、強制類型轉(zhuǎn)換錯誤除呵、除0等等再菊。出現(xiàn)了運行時異常,一般是程序的邏輯有問題颜曾,是程序自身的問題而非外部因素纠拔。

其他異常:Exception中除了運行時異常之外的,都屬于其他異常泛豪。也可以稱之為編譯時異常稠诲,這部分異常編譯器要求必須處置。這部分異常常常是因為外部運行環(huán)境導致诡曙,因為程序可能運行在各種環(huán)境中臀叙,如打開一個不存在的文件,此時拋出FileNotFoundException价卤。編譯器要求Java程序必須捕獲或聲明所有的編譯時異常劝萤,強制要求程序為可能出現(xiàn)的異常做準備工作。

不要被運行時異常的名稱所迷惑慎璧,理論上所有的錯誤都是運行時發(fā)生的床嫌。包括Error、RuntimeException胸私、編譯時異常等等厌处。所有的這些都只能在程序運行的過程中才能碰到。編譯時異常指的是編譯器要求必須處理的異常岁疼,并不是代碼編譯間發(fā)生的錯誤阔涉。

受檢異常和非受檢異常

字面理解,接受檢查的異常和不接受檢查的異常捷绒。根據(jù)上面的信息瑰排,Error和RuntimeException運行時異常都不能被檢查。他們都是程序運行過程中所產(chǎn)生的疙驾,只是Error的錯誤比較嚴重凶伙,一般是JVM產(chǎn)生,而RuntimeException一般是程序邏輯自身的問題它碎。受檢異常就是上面的編譯時異常函荣,這些異常在編譯時被強制要求捕獲或者聲明显押。編譯器將會為所有的受檢異常提供異常處理器。

PS:其實異常發(fā)生了傻挂,除了更改程序或者配置等乘碑,沒有其他的方法。只是對引起程序不正常工作的原因進行分類金拒,就出現(xiàn)了Error和Exception(運行時異常和編譯時異常)兽肤。這種分類能讓程序員更好的定位因此錯誤的原因,更方便更高效的進行開發(fā)绪抛,寫出健壯性更好的代碼资铡。但是異常并不能改變當前運行的結(jié)果,因為從程序開始運行的那一刻幢码,整個邏輯和數(shù)據(jù)都已經(jīng)固定了笤休。

異常處理機制

主要是try-catch-finally和throw、throws關鍵字症副。

異常都是派生于Throwable類的一個實例店雅,這個實例可以由JVM產(chǎn)生,也可以在程序中手動創(chuàng)建贞铣,用throw手動拋出闹啦。throw的對象必須是派生于Throwable類的實例,其他類型無法通過編譯辕坝。

受檢異常只有兩種選擇窍奋。要么被捕獲處理,要么被拋出酱畅,讓調(diào)用者處理费变。而非受檢異常沒有此強制要求。

throws是用來聲明異常圣贸,只要是派生于Throwable類的都可以被聲明。方法應該在其首部聲明所有可能拋出的受檢異常扛稽,這是強制要求的吁峻。非受檢異常不要求必須通過throws聲明,因為Error發(fā)生后對其無能為力在张,而如果有運行時異常用含,那么就是程序自身的問題,應該把時間花在修正程序的錯誤上帮匾,而不是說明程序發(fā)生的可能性上啄骇。所以編寫程序的時候throws關注點是受檢異常,但是非受檢異常也可以通過throws聲明瘟斜,只是不強制要求而已缸夹。若有多個受檢異常痪寻,必須在throws中全部聲明,如果方法沒有聲明所有可能的受檢異常虽惭,編譯器就會發(fā)出錯誤提示橡类。

try-catch-finally用來捕獲異常。所有派生于Throwable類都可以通過catch捕獲芽唇,try中放可能存在異常的方法顾画,如果在 try語句塊中的任何代碼拋出了一個在 catch 子句中說明的異常類,那么程序?qū)⑻^ try語句塊的其余代碼匆笤,并且執(zhí)行 catch 子句中的處理器代碼研侣。如果在 try 語句塊中的代碼沒有拋出任何異常,那么程序?qū)⑻^ catch 子句炮捧,如果方法中的任何代碼拋出了一個在 catch 子句中沒有聲明的異常類型庶诡,那么這個方法就會立刻退出。

在一個 try 語句塊中可以捕獲多個異常類型寓盗,并對不同類型的異常做出不同的處理灌砖。可以為每個異常類型使用一個單獨的 catch 子句傀蚌。需要注意基显,如果多個catch中的異常非繼承關系,那么catch順序不影響結(jié)果善炫,如果catch異常存在類繼承關系撩幽,那么子類的catch應該放在前面,父類的在后面箩艺。如下圖窜醉,catch的判斷是從上到下,如果父類在前艺谆,那么派生于父類的都會被捕獲榨惰,執(zhí)行父類的處理代碼爷狈,這樣導致后面子類的處理代碼永遠不會被執(zhí)行板熊。

finally一般用來關閉所占用的資源猛频。如果代碼拋出異常坝撑,就會終止剩余代碼的處理博投,并且退出這個方法交煞。這樣可能會導致一些程序占用的系統(tǒng)并不能被正確的釋放纺座。而不管是否有異常被捕獲硝枉,finally中子句的代碼都會被執(zhí)行抹估,可以在這里正確的釋放資源缠黍。

抓拋模型

所有的異常,一定是先有一個實例然后拋出药蜻。這個可以JVM完成瓷式,也可以手動執(zhí)行替饿。這個實例是整個異常處理的源頭。然后調(diào)用類可以利用try-catch-finally對異常進行捕獲蒿往,也可以在方法首部通過throws繼續(xù)向上層拋出盛垦。try-catch-finally是抓,throw/throws是拋瓤漏。一個類遇到異常腾夯,要么捕獲后處理,要么繼續(xù)向上拋出蔬充,讓調(diào)用者進行處理蝶俱。

catch中可以為空,這樣程序就會忽略掉哪些異常饥漫。不進行處理本身就是一種處理方式榨呆。在catch中也可能拋出異常,也可以手動拋出庸队。這樣可以改變異常的類型积蜻,使用異常的包裝技術(shù),原先的異常時新異常產(chǎn)生的原因彻消,可以讓用戶拋出子系統(tǒng)中的高級異常竿拆,而不會丟失原始異常的細節(jié)。

try-catch-finally可以靈活組合宾尚”瘢可以try-catch、try-finally或者try-catch-finally煌贴∮澹可以分為以下幾種情況

1.try中執(zhí)行正常,忽略catch牛郑,最后執(zhí)行finally怠肋。

2.try中出現(xiàn)異常,且異常在catch中聲明淹朋,執(zhí)行catch,最后執(zhí)行finally.

3.try中出現(xiàn)異常灶似,但catch中未聲明,不執(zhí)行catch瑞你,最后執(zhí)行finally.

4.try中出現(xiàn)異常,且異常在catch中聲明希痴,執(zhí)行catch時出現(xiàn)異常者甲,停止catch中代碼,執(zhí)行finally并且拋出catch中新出現(xiàn)的異常砌创。

finally

finally不管是try或者catch中沒有由異常虏缸,最后大概率都會執(zhí)行鲫懒。這是因為編譯器會講 finally 塊中的代碼復制兩份并分別添加在 try 和 catch 的后面。但是如果try或者catch中出現(xiàn)了 System.exit()語句刽辙,則會直接退出窥岩,并不會執(zhí)行finally模塊。

finally中沒有return語句 最后返回值為2

finally中有return語句 最后返回值為3

上面兩個代碼只是在finally中是否存在return語句的區(qū)別宰缤,但直接結(jié)果卻并不相同颂翼。可以看到如果finally中沒有return語句慨灭,程序就會把finally中的操縱數(shù)據(jù)忽略掉朦乏。其實finally中的數(shù)據(jù)操作也是執(zhí)行了的,但是并沒有返回氧骤。這是因為在return語句返回之前呻疹,虛擬機會將待返回的值壓入操作數(shù)棧,等待返回筹陵。即使 finally 語句塊對 i 進行了修改刽锤,但是待返回的值已經(jīng)確實的存在于操作數(shù)棧中了,所以不會影響程序返回結(jié)果朦佩。

在try中的數(shù)據(jù)處理完并思,檢測到有return語句時,會先將數(shù)據(jù)壓入操作數(shù)棧等待返回吕粗,然后去執(zhí)行finally語句纺荧,如果finally語句沒有將新的結(jié)果壓入操作數(shù)棧,那么只可能返回原先的結(jié)果颅筋。從這個角度理解宙暇,即使finally中對數(shù)據(jù)處理,但是返回的依舊時try中的“臟數(shù)據(jù)”议泵。finally并不是沒有執(zhí)行占贫,而是執(zhí)行了卻沒有沒返回。添加return語句則會將finally處理過的數(shù)據(jù)壓入操作數(shù)棧返回先口,原先的“臟數(shù)據(jù)”失效型奥。

注意這里指的是基本變量,如果是引用類型不受此影響碉京。因為不管在哪里進行運算厢汹,處理的都是引用背后的“實體”。

Throwable

Throwable是頂層的異常類谐宙,下面是 Throwable 類的主要方法:

1.public String getMessage()

返回關于發(fā)生的異常的詳細信息烫葬。這個消息在Throwable 類的構(gòu)造函數(shù)中初始化了

2.public Throwable getCause()

返回一個Throwable 對象代表異常原因

3.public String toString()

使用getMessage()的結(jié)果返回類的串級名字

4.public void printStackTrace()

打印toString()結(jié)果和棧層次到System.err,即錯誤輸出流

5.public StackTraceElement [] getStackTrace()

返回一個包含堆棧層次的數(shù)組。下標為0的元素代表棧頂搭综,最后一個元素代表方法調(diào)用堆棧的棧底

6.public Throwable fillInStackTrace()

用當前的調(diào)用棧層次填充Throwable 對象棧層次垢箕,添加到棧層次任何先前信息中

自定義異常

Java 的異常機制中所定義的所有異常不可能預見所有可能出現(xiàn)的錯誤,某些特定的情境下兑巾,則需要我們自定義異常類型來向上報告某些錯誤信息条获。

一般地,用戶自定義異常類都是RuntimeException的子類

自定義異常類通常需要編寫幾個重載的構(gòu)造器

自定義異常需要提供 serialVersionUID 序列化唯一ID蒋歌,方便調(diào)試

自定義異常最重要的是異常類的名字帅掘,當異常出現(xiàn)時,可以根據(jù) 名字判斷異常類型

注意事項

當子類重寫父類帶有throws聲明的函數(shù)時奋姿,其聲明的異常范圍必須要在父類的支持范圍內(nèi)锄开,即范圍不能比父類大。只能保持范圍不變或者更精確称诗,不能變大萍悴。如果父類沒有throws,那么子類也不能有throws寓免,受檢異常必須在子類內(nèi)部捕獲處理癣诱。

Java程序可以是多線程的。每一個線程都是一個獨立的執(zhí)行流袜香,獨立的函數(shù)調(diào)用棧撕予。如果程序只有一個線程,那么沒有被任何代碼處理的異常 會導致程序終止蜈首。如果是多線程的实抡,那么沒有被任何代碼處理的異常僅僅會導致異常所在的線程結(jié)束。也就是說欢策,Java中的異常是線程獨立的吆寨,線程的問題應該由線程自己來解決,而不要委托到外部踩寇,也不會直接影響到其它線程的執(zhí)行啄清。

有興趣了解更多編程基礎知識可以觀看教學視頻繼續(xù)學習。異常處理不能代替簡單的測試俺孙。捕獲異常所花費的時間要遠遠的超過了測試的時間辣卒,所以在可能的情況下,先對執(zhí)行條件進行判斷睛榄,而不要等執(zhí)行出錯之后捕獲異常荣茫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市场靴,隨后出現(xiàn)的幾起案子啡莉,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件票罐,死亡現(xiàn)場離奇詭異,居然都是意外死亡泞边,警方通過查閱死者的電腦和手機该押,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阵谚,“玉大人蚕礼,你說我怎么就攤上這事∩沂玻” “怎么了奠蹬?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嗡午。 經(jīng)常有香客問我囤躁,道長,這世上最難降的妖魔是什么荔睹? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任狸演,我火速辦了婚禮,結(jié)果婚禮上僻他,老公的妹妹穿的比我還像新娘宵距。我一直安慰自己,他們只是感情好吨拗,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布满哪。 她就那樣靜靜地躺著,像睡著了一般劝篷。 火紅的嫁衣襯著肌膚如雪哨鸭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天携龟,我揣著相機與錄音兔跌,去河邊找鬼。 笑死峡蟋,一個胖子當著我的面吹牛坟桅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蕊蝗,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼仅乓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蓬戚?” 一聲冷哼從身側(cè)響起夸楣,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后豫喧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體石洗,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年紧显,在試婚紗的時候發(fā)現(xiàn)自己被綠了讲衫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡孵班,死狀恐怖涉兽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情篙程,我是刑警寧澤枷畏,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站虱饿,受9級特大地震影響拥诡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜郭厌,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一袋倔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧折柠,春花似錦宾娜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至承冰,卻和暖如春华弓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背困乒。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工寂屏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人娜搂。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓迁霎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親百宇。 傳聞我的和親對象是個殘疾皇子考廉,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容