1.什么是Java異常?
? ? ? ?今天我們來(lái)聊聊java異常,異常時(shí)導(dǎo)致程序中斷執(zhí)行的一種指令流包雀。我們?cè)谔岣叽a穩(wěn)定性和健壯性的時(shí)候,常常會(huì)花更多的時(shí)間去考慮亲铡,代碼可能存在的異常才写。編碼中對(duì)可能的發(fā)生的異常先一步正確處理,就可以確保異常不會(huì)導(dǎo)致程序不可用奖蔓。說(shuō)了這么多異常赞草,那么異常是什么呢?
? ? ? ?Java異常是一種導(dǎo)致程序中斷的執(zhí)行指令流吆鹤。它存在應(yīng)用服務(wù)器硬件錯(cuò)誤厨疙、程序編寫(xiě)的時(shí)候未考慮數(shù)組越界、也可能是網(wǎng)絡(luò)通信中網(wǎng)絡(luò)抖動(dòng)的客觀原因?qū)е碌漠惓5惹闆r疑务。異痴雌啵可能是主觀代碼設(shè)計(jì)不周全也可能是客觀硬件報(bào)錯(cuò)存哲,那么認(rèn)識(shí)異常就是避免異常的第一步。我們先從異常家族的認(rèn)識(shí)開(kāi)始弥喉,下面異常的族譜:
? ? ? ?Throwable類(lèi)作為所有異常的發(fā)源鲫竞,他是整個(gè)Java異常體系的超體,其下面分為Error和Exception兩大類(lèi)鞠鲜。
?? ? ? ?Error與其子類(lèi)實(shí)例代表嚴(yán)重系統(tǒng)錯(cuò)誤,應(yīng)用程序無(wú)法處理,如硬件層面的錯(cuò)誤嗤朴、JVM錯(cuò)誤或內(nèi)存不足等問(wèn)題,這種錯(cuò)誤發(fā)生時(shí)Java應(yīng)用程序本身無(wú)力恢復(fù)虫溜。Error對(duì)象拋出時(shí)雹姊,基本上不用處理,任其傳播至JVM為止衡楞,應(yīng)用程序能做的最多留下日志信息吱雏。
? ? ? ?Exception代表程序運(yùn)行時(shí)發(fā)送的各種不期望發(fā)生的事件。可以被Java異常處理機(jī)制使用歧杏,是異常處理的核心镰惦。Exception及其子類(lèi)可以被程序處理,也就是所在應(yīng)用程序中正確的處理了Exception及其子類(lèi)就能提高代碼的穩(wěn)定性和健壯性犬绒。
? ? ? ?Exception類(lèi)下面分為檢測(cè)異常和非檢測(cè)異常:
? ? ? ?檢測(cè)異常旺入,是JVM強(qiáng)制要求程序員為可能出現(xiàn)的異常做預(yù)備處理工作。JVM規(guī)定檢測(cè)異常要么使用try-catch語(yǔ)句捕獲它并進(jìn)行處理凯力,要么使用throws子句聲明并拋出它茵瘾,否者javac在編譯程序會(huì)不通過(guò)。這類(lèi)異常一般是程序運(yùn)行環(huán)境導(dǎo)致的咐鹤,為了預(yù)防未知環(huán)境對(duì)程序的影響拗秘,規(guī)定程序員需要處理這類(lèi)異常。
? ? ? ?非檢測(cè)異常祈惶,javac在編譯時(shí)聘殖,不會(huì)提示和發(fā)現(xiàn)異常的存在,JVM不強(qiáng)制要求程序員處理這樣的異常行瑞。當(dāng)然奸腺,作為程序員我們應(yīng)該預(yù)防這樣的異常導(dǎo)致程序崩潰,所以建議使用try-catch-finally處理它血久。這類(lèi)異常原因多半是代碼寫(xiě)的邏輯有問(wèn)題或考慮不周全突照。
2.當(dāng)一個(gè)Exception在程序中發(fā)生的時(shí)候,JVM是怎么做的呢氧吐?
? ? ? ?對(duì)java整體異常家族有了大致的認(rèn)識(shí)之后讹蘑,那么當(dāng)一個(gè)Exception在程序中發(fā)生的時(shí)候,JVM是怎么做的呢筑舅?
? ? ? ?認(rèn)識(shí)這個(gè)問(wèn)題座慰,我們首看看JVM是如何執(zhí)行java代碼的,按照?qǐng)?zhí)行步驟可以分為翠拣,JVM將class文件轉(zhuǎn)換為JVM的java類(lèi)版仔。JVM為java類(lèi)建立方法表,方法表中的索引可以引導(dǎo)JVM找到需要執(zhí)行的方法體和JVM方法執(zhí)行棧误墓。當(dāng)然蛮粮,invokemethod調(diào)用執(zhí)行一個(gè)方法的時(shí)候,Java虛擬機(jī)把描述該方法的棧結(jié)構(gòu)置入方法執(zhí)行棧棧頂谜慌,位于棧頂?shù)姆椒檎趫?zhí)行的方法然想。JVM會(huì)為每一個(gè)方法建立執(zhí)行的堆和棧用于存放執(zhí)行中的變量,如果此時(shí)程序出現(xiàn)一個(gè)Exception欣范,拋出的異常先轉(zhuǎn)移給合適的異常處理語(yǔ)句变泄。代碼的執(zhí)行會(huì)被相應(yīng)的Exception執(zhí)行流接管令哟,將方法表信息、發(fā)生異常的位置信息和方法的堆棧信息壓入Exception的堆棧妨蛹,當(dāng)程序中斷的時(shí)候IDE就會(huì)將Exception的堆棧信息打印出來(lái)励饵。
? ? ? ?如果我們通過(guò)try…..catch….finally語(yǔ)句來(lái)處理異常,處理流程又是怎樣的呢滑燃?在try代碼塊中拋出的異常役听,代碼執(zhí)行流會(huì)跳轉(zhuǎn)到catch代碼塊執(zhí)行,catch代碼塊可以對(duì)發(fā)生的異常進(jìn)行補(bǔ)救表窘。讓代碼回歸到正常的執(zhí)行流程典予。finally語(yǔ)句,無(wú)論在try模塊中是否發(fā)生異常乐严,都會(huì)執(zhí)行finally語(yǔ)句瘤袖,使用finally語(yǔ)句主要是為了釋放被占用的資源,比如打開(kāi)的文件或鏈接的通信資源等昂验。
? ? ? ?總體來(lái)說(shuō)捂敌,Java語(yǔ)言的異常處理流程,從程序中獲取異常信息既琴。根據(jù)Java的源文件和用戶(hù)調(diào)用的包列表占婉,JVM可以獲取該程序包括Java API所引發(fā)的異常在內(nèi)的異常處理的信息,這些信息包括:異常引發(fā)位置甫恩、異常拋出順序逆济、引發(fā)異常的方法名和類(lèi)名等。這些異常信息在程序異常的排查和修改的時(shí)候非常重要磺箕,后面會(huì)講到如何正確處理這些異常奖慌。
3.當(dāng)我們編寫(xiě)程序的時(shí)候如何對(duì)待可能出現(xiàn)的異常呢?
? ? ? ?通常在發(fā)生異常的時(shí)候我們有兩種處理模型:終止與恢復(fù)松靡。
? ? ? ?終止模型:前提是假設(shè)錯(cuò)誤非常關(guān)鍵简僧,以至于程序無(wú)法返回到程序正常運(yùn)行軌跡,一旦異常拋出就意味著程序?qū)⑼V固峁┓?wù)雕欺。如:數(shù)據(jù)庫(kù)連接異常發(fā)生
? ? ? ?恢復(fù)模型:也就是異常程序發(fā)生錯(cuò)誤岛马,錯(cuò)誤可以被修復(fù)然后重新回到正常程序執(zhí)行的軌跡上。例如阅茶,我們可以將數(shù)據(jù)庫(kù)連接try蛛枚。。脸哀。catch置于循環(huán)中,一次連接不成功可以循環(huán)下一次進(jìn)行連接扭吁。
? ? ? ?配合終止和恢復(fù)模型撞蜂,我們會(huì)配合使用throw和throws語(yǔ)法盲镶。throws關(guān)鍵字主要在方法簽名中使用,用于聲明該方法可能拋出的異常蝌诡。throws 可以理解成是一種通知行為溉贿,沒(méi)有實(shí)際的拋出異常的動(dòng)作,而僅僅是告訴調(diào)用他的上層函數(shù)浦旱,這里可能會(huì)拋出這個(gè)異常;
? ? ? ?throw用于在函數(shù)體內(nèi)語(yǔ)句中宇色,表示拋出一個(gè)實(shí)際的異常的實(shí)際動(dòng)作,如果在函數(shù)內(nèi)沒(méi)有捕獲并處理颁湖,那么將會(huì)一直向上拋出這個(gè)異常直到被main()/Thread.run()拋出宣蠕。
? ? ? ?我們了解了Java的異常內(nèi)容之后,在程序中遵循怎樣的行業(yè)規(guī)則和大牛的經(jīng)驗(yàn)合理使用異常甥捺,提高代碼的穩(wěn)定性呢抢蚀?
? ? ? ?下面整理了包括Effective Java異常使用指導(dǎo)原則和網(wǎng)上博客內(nèi)容:
1.? ?不要講異常處理用于正常的控制流(設(shè)計(jì)良好的API不應(yīng)該強(qiáng)迫它的調(diào)用者為了正常的控制流而使用異常)。
2.? ? 對(duì)可以恢復(fù)的情況使用了受檢異常镰禾,對(duì)編程錯(cuò)誤使用運(yùn)行時(shí)異常皿曲。
3.? ? 避免不必要的使用受檢異常(可以通過(guò)一些狀態(tài)檢測(cè)手段來(lái)避免異常的發(fā)生)。
4.? ? 優(yōu)先使用標(biāo)準(zhǔn)異常吴侦。
5.? ? 每個(gè)方法拋出的異常都要有文檔屋休。
6.? ? 保持異常的原子性。
7.? ? 不要再catch中忽略掉捕獲到的異常备韧。
8.? ? 處理運(yùn)行時(shí)異常博投,采用邏輯合理規(guī)避同時(shí)輔助try…catch處理。
9.? ? 在多重catch快后面盯蝴,可以加一個(gè)catch(Exception)來(lái)處理可能會(huì)被遺漏的異常毅哗。
10.? 對(duì)于不確定的代碼,也可以加上try…catch捧挺,處理潛在的異常虑绵。
11.? 盡量去處理異常,切忌只是簡(jiǎn)單的調(diào)用printStackTrace()打印輸出闽烙。
12.? 具體如何處理異常翅睛,要根據(jù)不同的業(yè)務(wù)需求和異常類(lèi)型去決定。
13.? 盡量添加finally語(yǔ)句塊去釋放占用的資源黑竞。
? ? ? ?不要被這么多規(guī)則嚇到了捕发,不需要逐條強(qiáng)記他們。我對(duì)異常的理解很魂,首先態(tài)度上要謙虛明白代碼中很難避免因?yàn)榭紤]不周出現(xiàn)的異常扎酷,所以編碼時(shí)應(yīng)該采用防衛(wèi)式編碼。其次在編碼處理過(guò)程中使用try…catch…finally語(yǔ)句預(yù)防可能的異常遏匆,采用合適的異常處理模式對(duì)待程序可能的異常法挨。