Java受檢異常的一些思考
什么是異常?
要了解受檢異常,首先要了解什么是異常。
在Java中,異常是一套能夠一致地處理錯(cuò)誤和恢復(fù)代碼運(yùn)行正常的機(jī)制。
在C語(yǔ)言中,他沒(méi)有異常處理機(jī)制。如果一個(gè)函數(shù)出現(xiàn)了異常情況,例如一個(gè)除法的函數(shù),被除數(shù)輸入了0,這個(gè)時(shí)候需要把這個(gè)異常告訴函數(shù)調(diào)用者,一般情況下我們會(huì)通過(guò)返回一個(gè)特殊的值來(lái)告訴調(diào)用者出現(xiàn)異常,如返回-1。這是c語(yǔ)言常規(guī)的處理異常手法。這會(huì)引發(fā)另一個(gè)問(wèn)題:我們需要在每次調(diào)用函數(shù)的時(shí)候,都進(jìn)行條件判斷,看返回值是否出現(xiàn)了異常,這樣會(huì)讓我們的代碼出現(xiàn)了非常多的if判斷語(yǔ)句?!?a target="_blank" href="http://www.dbjr.com.cn/books/75542.html">Java編程思想》一書(shū)中講到:每次調(diào)用的時(shí)候都必須執(zhí)行條件測(cè)試,以確定會(huì)產(chǎn)生何種結(jié)果。這使程序難以閱讀并且有可能降低運(yùn)行效率,因此程序員們既不愿意指出,也不愿意處理異常
不僅代碼不美觀和寫(xiě)很多的代碼,而且容易漏掉一些情況沒(méi)有進(jìn)行判斷,沒(méi)有安全感。
這個(gè)是否需要有一套完整的異常處理機(jī)制,當(dāng)出現(xiàn)問(wèn)題的時(shí)候,把異常交給機(jī)制去處理,這樣我們就可以把處理異常的代碼和正常的代碼分開(kāi),且不擔(dān)心出現(xiàn)“漏網(wǎng)之魚(yú)”。因而我們需要:
一套統(tǒng)一的錯(cuò)誤報(bào)告機(jī)制,可以減少代碼量、代碼簡(jiǎn)潔的同時(shí),能夠把錯(cuò)誤進(jìn)行統(tǒng)一處理,不會(huì)有漏網(wǎng)之魚(yú)。
關(guān)于這個(gè)問(wèn)題在《Java編程思想》中是這樣說(shuō)的:異常往往能降低錯(cuò)誤處理代碼的復(fù)雜度。如果不使用異常,那么就必須檢查特定的錯(cuò)誤,并在程序中的許多地方去處理它。而如果使用異常,那就不必在方法調(diào)用處進(jìn)行檢查,因?yàn)楫惓C(jī)制將保證能夠捕獲這個(gè)錯(cuò)誤。理想情況下,只需在一個(gè)地方處理錯(cuò)誤,即所謂的異常處理程序中。這種方式不僅節(jié)省代碼,而且把“描述在正常執(zhí)行過(guò)程中做什么事”的代碼和“出了問(wèn)題怎么辦”的代碼相分離??傊?,與以前的錯(cuò)誤處理方法相比,異常機(jī)制使代碼的閱讀、編寫(xiě)和調(diào)試工作更加井井有條。Java異常機(jī)制比常規(guī)的錯(cuò)誤報(bào)告機(jī)制還做到了:
我們可以在一個(gè)地方集中處理錯(cuò)誤,把正常的代碼處理邏輯和錯(cuò)誤情況處理邏輯分離,使用代碼的閱讀、編寫(xiě)和調(diào)試更加井井有條。
編譯時(shí)還是運(yùn)行時(shí)?
熟悉Java開(kāi)發(fā)的讀者知道Java異常機(jī)制擁有兩種大類(lèi)型的異常:運(yùn)行時(shí)異常和編譯時(shí)異常。
編譯時(shí)異常是在編譯的時(shí)候就能被檢查出來(lái)的異常,也被稱為受檢異常(Checked Exception)。如IOException。運(yùn)行時(shí)異常就是在運(yùn)行的時(shí)候才會(huì)被檢查的異常,如空指針異常。
而我們今天要討論的就是關(guān)于運(yùn)行時(shí)異常。這兩種異常的本質(zhì)差別是:檢查的時(shí)機(jī)。一般情況來(lái)說(shuō),錯(cuò)誤能在編譯階段就被檢查出來(lái)是最好的,編譯器幫我們解決錯(cuò)誤。然而情況也不一定都是這樣,因?yàn)樘幚懋惓P枰覀儗?xiě)出更多的代碼。如果每次調(diào)用一個(gè)方法都需要處理所有的異常,那么我們需要的工作量就太大了。例如空指針,我們知道幾乎每個(gè)方法都會(huì)拋出這個(gè)異常,但是java并沒(méi)有強(qiáng)制我們?nèi)ゲ东@他。所以異常處理時(shí)機(jī)的分類(lèi)是一個(gè)權(quán)衡的結(jié)果,既想讓編譯器盡多地幫我們檢查錯(cuò)誤,又不能讓開(kāi)發(fā)者代碼編寫(xiě)太多額外的代碼,需要在這兩者之間找到一個(gè)最平衡的點(diǎn)。
“受檢異?!本烤箍刹豢扇??
這個(gè)問(wèn)題其實(shí)爭(zhēng)論的歷史非常久,新型語(yǔ)言Kotlin中就把受檢異常去掉了。這樣我們就沒(méi)必要再去顯示捕獲任何異常。這在我第一次接觸kotlin的時(shí)候感覺(jué)簡(jiǎn)直是一個(gè)噩夢(mèng):
因?yàn)槲也恢勒{(diào)用的方法是否會(huì)拋出異常、拋出什么異常,那么我可能會(huì)忽略一些關(guān)鍵的異常沒(méi)有捕獲而導(dǎo)致代碼的健壯性降低。
這也確實(shí)也是很多支持Java受檢異常開(kāi)發(fā)者的心聲。有了受檢異常,那么編譯器會(huì)提醒我們我們調(diào)用的這個(gè)方法會(huì)拋出什么異常,我們需要去使用try-catch來(lái)捕獲他,不會(huì)遺漏任何重要的異常。但事實(shí)真的如此嗎?
受檢異常真的能夠避免所有的異常嗎?不是的,我們依舊會(huì)出現(xiàn)空指針異常、非法狀態(tài)異常等,我們并不會(huì)去顯示捕獲這一類(lèi)的異常,他在java中稱為運(yùn)行時(shí)異常。那有讀者可能會(huì)說(shuō):受檢異常是可以讓我們避免一些方法顯示拋出的異常,并不是要處理所有的錯(cuò)誤。因而,我們必須呀承認(rèn)一個(gè)事實(shí):無(wú)論是否有受檢異常,均不能解決所有的異常,兩者的差別是編譯階段處理異常的數(shù)量。而這兩者的邊界是一種權(quán)衡的結(jié)果使用受檢異常,我們真的處理異常了嗎?
CLU 的設(shè)計(jì)師們認(rèn)為:我們覺(jué)得強(qiáng)迫程序員在不知道該采取什么措施的時(shí)候提供處理程序,是不現(xiàn)實(shí)的,只有在你知道如何處理的情況下才捕獲異常。
Stroustrup 這樣認(rèn)為:這樣一來(lái)幾乎所有的函數(shù)都得提供異常說(shuō)明了,也就都得重新編譯,而且還會(huì)妨礙它同其他語(yǔ)言的交互。這樣會(huì)迫使程序員違反異常處理機(jī)制的約束,他們會(huì)寫(xiě)欺騙程序來(lái)掩蓋異常。這將給沒(méi)有注意到這些異常的人造成一種虛假的安全感
我們會(huì)發(fā)現(xiàn)現(xiàn)在的一種情況是:開(kāi)發(fā)者捕獲異常之后,并沒(méi)有做具體的處理,而是對(duì)異常進(jìn)行打印,或者干脆不做任何處理,這是不符合異常的規(guī)則的。只有知道如何處理異常的情況下才去捕獲異常。如果捕獲異常之后卻不做任何處理,那么實(shí)際上是把異?!俺粤恕薄R?yàn)槿绻惓0l(fā)生了,我們以為我們處理了,但事實(shí)上我們并沒(méi)有做任何處理,那么這個(gè)錯(cuò)誤就會(huì)一直存在,反而更不利于我們對(duì)代碼健壯性的維護(hù)。有讀者可能會(huì)覺(jué)得,我們對(duì)于不知如何處理的異??梢酝蠏仯@會(huì)導(dǎo)致另一個(gè)問(wèn)題:頂層接口的調(diào)用者會(huì)拿到非常多的異常類(lèi)型而不知如何處理,最終只能向上拋給控制臺(tái),從而使得整套異常機(jī)制完全失效。
我們知道異常機(jī)制的目的之一就是為了減少代碼,不用每次調(diào)用一個(gè)方法都去進(jìn)行條件判斷,而通過(guò)使用try-catch就可以把錯(cuò)誤進(jìn)行集中處理,把正常代碼的邏輯和處理異常的邏輯分離開(kāi)來(lái),但是,受檢異常的存在,當(dāng)我們即使調(diào)用一個(gè)Thread.sleep()方法都必須進(jìn)行try-catch,又如IO流的開(kāi)啟與關(guān)閉也都必須進(jìn)行try-catch,,這樣的代碼量其實(shí)又回到原地了,并沒(méi)有達(dá)到最初的目標(biāo):減少代碼量。
《Java編程思想》一書(shū)認(rèn)為:不在于編譯器是否會(huì)強(qiáng)制程序員去處理錯(cuò)誤,而是要有一致的、使用異常來(lái)報(bào)告錯(cuò)誤的模型;不在于什么時(shí)候進(jìn)行檢查,而是一定要有類(lèi)型檢查。也就是說(shuō),必須強(qiáng)制程序使用正確的類(lèi)型,至于這種強(qiáng)制施加于編譯時(shí)還是運(yùn)行時(shí),那倒沒(méi)關(guān)系。
即異常機(jī)制的重點(diǎn)在于一致的使用報(bào)告錯(cuò)誤的模式和強(qiáng)制程序使用正確的類(lèi)型。所以異常的存在,并不是要我們?cè)诰幾g時(shí)把所有異常都處理了,而是我們可以統(tǒng)一處理異常且讓程序使用正確的類(lèi)型。
我的觀點(diǎn)
上面講到我第一次接觸到kotlin的時(shí)候非常反感他把checkedException去掉了,這讓我非常沒(méi)有安全感,我的原因是:調(diào)用別人的接口的時(shí)候,這時(shí)候沒(méi)有編譯器的提醒我會(huì)遺漏關(guān)鍵的異常而導(dǎo)致代碼的健壯性下降。但,正如上述,如果不知道如何處理異常而去捕獲異常,然后不做任何處理,這是在欺騙自己,獲得一種虛假的安全感。我們并沒(méi)有解決異常,而是把異?!俺粤恕保雌饋?lái)程序并沒(méi)有崩潰,但事實(shí)上我們只是把異常藏起來(lái)了,沒(méi)有做任何處理。這種情況不如把異常往上拋到最頂層交給控制臺(tái)處理,我們知道程序崩潰了,那么我們就知道得去解決這種異常,這樣才能真正促進(jìn)代碼的健壯性。
而向上拋會(huì)會(huì)導(dǎo)致另一個(gè)問(wèn)題:頂層接口的調(diào)用者會(huì)拿到非常多的異常而不知道如何處理,再次向上拋,最終導(dǎo)致整個(gè)異常機(jī)制失效。而如果每一層都只處理自己感興趣的異常,剩下的異常就會(huì)自動(dòng)拋到最頂層,代碼會(huì)更加簡(jiǎn)潔,我們也可以更加專(zhuān)注我們要處理的異常。
這兩個(gè)是我覺(jué)得“受檢異?!钡挠矀?,。而其他,如頻繁try-catch導(dǎo)致代碼量增多,兩種類(lèi)型的代碼導(dǎo)致邏輯不連貫等,相對(duì)來(lái)說(shuō)這屬于軟傷,在面對(duì)“可以在編譯階段解決更多的異常并沒(méi)有更強(qiáng)的說(shuō)服力。
去掉受檢異常對(duì)于java工程師來(lái)說(shuō)可能會(huì)很沒(méi)有安全感,因?yàn)椋簳?huì)存在一些異常沒(méi)有處理,他們就像一些炸彈,但不知道他們什么時(shí)候爆炸。這確實(shí)是個(gè)問(wèn)題,沒(méi)有受檢異常會(huì)有更多的異常留到運(yùn)行時(shí)。但這個(gè)問(wèn)題影響的效果可能并沒(méi)有我們想象中的那么大。我們真正處理受檢異常機(jī)制提醒的異常其實(shí)并不多,正如我上面所講,如果我們捕獲異常之后只是“吃掉它”,那么還不如拋給控制臺(tái)。
那么“受檢異常”是不是就一無(wú)是處?我覺(jué)得不是的。受檢異常的目的是在編譯階段處理異常,去掉確實(shí)可以避免上述的兩個(gè)問(wèn)題,且讓代碼更加的簡(jiǎn)潔。但是換之的是需要給測(cè)試人員更多的壓力和開(kāi)發(fā)者自身的對(duì)代碼健壯性的處理,需要有更多的精力去解決沒(méi)有“受檢異?!睅?lái)的問(wèn)題。不是有“全局捕獲異常嗎”?頻繁的崩潰和維護(hù),并不是一個(gè)好的解決方案,如果能在編碼階段就把隱藏的問(wèn)題解決是最好不過(guò)的。
從我自己的經(jīng)歷來(lái)說(shuō),我使用java和kotlin語(yǔ)言均開(kāi)發(fā)過(guò)不同的項(xiàng)目,而沒(méi)有受檢異常并沒(méi)有給我?guī)?lái)多大的障礙,反而不用使用try-catch讓我在編寫(xiě)代碼的時(shí)候更加舒適。但因我項(xiàng)目較小,所以可能并不能作為一個(gè)示例來(lái)證明,僅作為一個(gè)參考。而kotlin在這方面有一個(gè)我認(rèn)為不好的特點(diǎn):沒(méi)有異常聲明。即如果我想要看我調(diào)用的方法究竟會(huì)拋出什么異常時(shí),需要一行一行代碼去查看,而無(wú)法通過(guò)異常聲明來(lái)得知。
所以,需要怎么做?我認(rèn)為這兩種解決方案各有千秋,看開(kāi)發(fā)者如何取舍。而我個(gè)人覺(jué)得這是這種異常機(jī)制的限制,我們需要的是對(duì)異常機(jī)制的改進(jìn),如Java有了進(jìn)行注解對(duì)異常進(jìn)行忽略,這是一種進(jìn)步。對(duì)這個(gè)問(wèn)題的討論正如對(duì)洋蔥好不好吃的討論是差不多的,不同的團(tuán)隊(duì)不同的需求取舍,會(huì)有不同的結(jié)果。討論這個(gè)問(wèn)題更重要的是了解不同的解決方案有不同的優(yōu)缺點(diǎn),我們?cè)陂_(kāi)發(fā)的時(shí)候,可以發(fā)揮不同處理方案的優(yōu)勢(shì),注意不要踩坑。
沒(méi)有最好的語(yǔ)言,只有更好的開(kāi)發(fā)者。如果我們能夠在每層都解決自己應(yīng)該負(fù)責(zé)解決的異常,那么頂層接口就不會(huì)有高達(dá)八十多種異常的出現(xiàn)。如果對(duì)每種異常捕獲之后都能夠做好對(duì)應(yīng)的處理,而不是把異常“吃掉”來(lái)獲得虛假的安全感,那么受檢異常也沒(méi)有那么多的問(wèn)題。如果我們可以更加規(guī)范地開(kāi)發(fā),那么受檢異常,事實(shí)上問(wèn)題也不會(huì)那么多。簡(jiǎn)單粗暴去除掉受檢異常,可以通過(guò)迎合大部分開(kāi)發(fā)者的惰性和不規(guī)范來(lái)解決受檢異常存在的問(wèn)題,但是這并不是最好的解決方法。所以我們能做的,不是去捍衛(wèi)不同的立場(chǎng),而是不斷地提升自己的代碼規(guī)范,通過(guò)代碼規(guī)范來(lái)增強(qiáng)代碼的健壯性。
以上就是Java受檢異常的一些思考的詳細(xì)內(nèi)容,更多關(guān)于Java受檢異常的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot實(shí)現(xiàn)前端驗(yàn)證碼圖片生成和校驗(yàn)
這篇文章主要為大家詳細(xì)介紹了SpringBoot實(shí)現(xiàn)前端驗(yàn)證碼圖片生成和校驗(yàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02SpringBoot3集成iText實(shí)現(xiàn)PDF導(dǎo)出功能
不知道小伙伴們?cè)陧?xiàng)目中有沒(méi)有遇到過(guò)導(dǎo)出 PDF 的需求,小編在之前的 tienchin 項(xiàng)目中有一個(gè)合同導(dǎo)出的功能,需要將文檔導(dǎo)出為PDF,將文檔導(dǎo)出為 PDF 有很多方案,不同方案的優(yōu)缺點(diǎn)也各不相同,今天小編就和大家演示一個(gè),感興趣的小伙伴跟著小編一起來(lái)看看吧2024-10-10java實(shí)現(xiàn)String類(lèi)型和Date類(lèi)型相互轉(zhuǎn)換
很多人表示,java將string類(lèi)型轉(zhuǎn)為date類(lèi)型不知道應(yīng)該怎樣做,本文就來(lái)介紹一下java實(shí)現(xiàn)String類(lèi)型和Date類(lèi)型相互轉(zhuǎn)換,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10Java基礎(chǔ)之重載(Overload)與重寫(xiě)(Override)詳解
這篇文章主要介紹了Java基礎(chǔ)之重載(Overload)與重寫(xiě)(Override)詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04MyBatis-Plus通用枚舉自動(dòng)關(guān)聯(lián)注入的實(shí)現(xiàn)
本文主要介紹了MyBatis-Plus通用枚舉自動(dòng)關(guān)聯(lián)注入的實(shí)現(xiàn),解決了繁瑣的配置,讓 mybatis 優(yōu)雅的使用枚舉屬性,感興趣的可以一起來(lái)了解一下2021-06-06十分簡(jiǎn)單易懂的Java應(yīng)用程序性能調(diào)優(yōu)技巧分享
這篇文章主要介紹了十分簡(jiǎn)單易懂的Java性能調(diào)優(yōu)技巧分享,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11Mybatis一級(jí)緩存和結(jié)合Spring Framework后失效的源碼探究
這篇文章主要介紹了Mybatis一級(jí)緩存和結(jié)合Spring Framework后失效的源碼探究,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04javaweb開(kāi)發(fā)提高效率利器JRebel詳解
這篇文章主要介紹了javaweb開(kāi)發(fā)提高效率利器JRebel詳解,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04