java如何優(yōu)雅的處理異常 Java是c 實(shí)現(xiàn)的,為啥Java的異常處理更完善?
Java是c 實(shí)現(xiàn)的,為啥Java的異常處理更完善?這話很沒(méi)理由,那第一個(gè)低級(jí)語(yǔ)言編譯器應(yīng)該機(jī)器語(yǔ)言實(shí)現(xiàn)的呢,為啥功能越來(lái)越大系統(tǒng)完善?現(xiàn)在的編譯器很多都是自舉,用老版本開發(fā)新版本,功能又是更加完善
Java是c 實(shí)現(xiàn)的,為啥Java的異常處理更完善?
這話很沒(méi)理由,那第一個(gè)低級(jí)語(yǔ)言編譯器應(yīng)該機(jī)器語(yǔ)言實(shí)現(xiàn)的呢,為啥功能越來(lái)越大系統(tǒng)完善?
現(xiàn)在的編譯器很多都是自舉,用老版本開發(fā)新版本,功能又是更加完善系統(tǒng)
當(dāng)然是越標(biāo)準(zhǔn)封裝越系統(tǒng)完善啊,這跟用什么實(shí)現(xiàn)方法完全沒(méi)有關(guān)系,不是他所有的機(jī)制一定會(huì)是底層直接可以提供的
Java的trycatch也可以能捕捉全部異常,c的trycatch沒(méi)有辦法去捕獲部分十分,Java是怎么能夠做到的?
究竟你說(shuō)的”更完善系統(tǒng)”具體指的是什么?你問(wèn)題不說(shuō)很清楚怎么回答我?
如何優(yōu)雅的設(shè)計(jì)Java異常?
異樣的類別
正如我們所明白的,java中的異樣的超類是(后文省略為Throwable),它有兩個(gè)都很有用的子類,(后文省略為Exception)和(后文省略為Error),其中Error由JVM虛擬機(jī)進(jìn)行管理,如我們所熟知的OutOfMemoryError十分等,因?yàn)槲覀儽疚牟涣私釫rror無(wú)比,那你我們細(xì)說(shuō)一下Exception異常。
Exception異樣有個(gè)比較好最重要的子類,叫做什么RuntimeException。我們將RuntimeException或其他繼承自RuntimeException的子類一般稱非受檢異樣(invalidException),其他繼承自Exception異樣的子類一般稱受檢十分(blockedException)。本文重點(diǎn)來(lái)關(guān)注看看受檢異常和非受檢異樣這兩種極其。
該如何選擇異常
從筆者的開發(fā)經(jīng)驗(yàn)來(lái)看,如果不是在一個(gè)應(yīng)用中,必須旗下一個(gè)方法(如某個(gè)功能的service方法),這個(gè)方法假如中間肯定出現(xiàn)異樣,這樣的話你必須判斷這個(gè)無(wú)比又出現(xiàn)之后有無(wú)全局函數(shù)者可以不如何處理,而且你是否需要如果能全局函數(shù)者并且處理,如果不是動(dòng)態(tài)鏈接庫(kù)者也可以如何處理,但是你也希望內(nèi)部函數(shù)者通過(guò)處理,那就就要拋出受檢異常,警告動(dòng)態(tài)創(chuàng)建者在不使用你的方法時(shí),判斷到要是擲下異常時(shí)如果接受處理。
幾乎完全一樣的,如果在寫某個(gè)方法時(shí),你以為這是個(gè)無(wú)意中十分,理論上說(shuō),你覺(jué)著運(yùn)行時(shí)很可能會(huì)出現(xiàn)什么問(wèn)題,而這些問(wèn)題或許并非定然再一次發(fā)生的,也不要全局函數(shù)者總是顯示的通過(guò)異常來(lái)確認(rèn)業(yè)務(wù)流程你操作的,那你這時(shí)就可以使用一個(gè)RuntimeException這樣的非受檢異樣.
再說(shuō),估計(jì)也我上邊說(shuō)的這段話,你讀了很多遍也始終總覺(jué)得艱澀了。
那么,請(qǐng)領(lǐng)著我的思路,在慢慢仔細(xì)體會(huì)幫一下忙。
什么時(shí)候才要拋異樣
首先我們是需要打聽一下一個(gè)問(wèn)題,什么時(shí)候才不需要拋異常?異常的設(shè)計(jì)是方便給開發(fā)者在用的,但并非亂是用,筆者相對(duì)于什么時(shí)候拋無(wú)比這個(gè)問(wèn)題也問(wèn)了很多朋友,能給出確切答案的倒是不多。反正這個(gè)問(wèn)題很簡(jiǎn)單,如果不是你總覺(jué)得某些”問(wèn)題”幫忙解決不了了,那就你就是可以拋出異常了。
比如,你在寫一個(gè)service,其中在寫到某段代碼處,你發(fā)現(xiàn)到很可能會(huì)有一種問(wèn)題,那么就請(qǐng)甩出異常吧,不會(huì)相信我,你此時(shí)甩出十分將是一個(gè)最佳的方法時(shí)機(jī)。
應(yīng)該要丟出整樣的極其
打聽一下完了什么時(shí)候才要一拋異常后,我們?cè)僬J(rèn)真思索一個(gè)問(wèn)題,真的當(dāng)我們拋出異常時(shí),我們應(yīng)該是選用比較整樣的極其呢?到底是是受檢十分還是非受檢異常呢(RuntimeException)呢?
我來(lái)通俗的解釋幫一下忙這個(gè)問(wèn)題,先從受檢異樣你說(shuō)起,假如有那樣的話一個(gè)業(yè)務(wù)邏輯,不需要從某文件中讀取數(shù)據(jù)某個(gè)數(shù)據(jù),這個(gè)加載能操作可能會(huì)是由于文件被刪除掉等其他問(wèn)題導(dǎo)致沒(méi)能獲取使再次出現(xiàn)讀取數(shù)據(jù)錯(cuò)誤,那么還得從redis或mysql數(shù)據(jù)庫(kù)中再去資源此數(shù)據(jù),參考萬(wàn)分感謝代碼,getKey(Integer)為入口程序.
可以了,看了以上代碼以后,你或許心中有一些想法,原來(lái)受檢十分這個(gè)可以控制義務(wù)邏輯,對(duì),是啊,按照受檢異常真的可以不操縱業(yè)務(wù)邏輯,可是千萬(wàn)記住千萬(wàn)不能這樣在用,我們應(yīng)該是合不合理的甩出異常,是因?yàn)槌绦虮旧聿攀橇鞒蹋悩拥淖饔脝螁问钱?dāng)你通過(guò)不出去的時(shí)候能找到的一個(gè)借口只不過(guò)是,它并不能不能以為壓制程序流程的入口或出口,如果不是這樣的使用的話,是在將異常的作用逐漸擴(kuò)大化,這樣的可以說(shuō)會(huì)造成代碼復(fù)雜程度的增加,耦合性會(huì)增加,代碼可讀性減低等問(wèn)題。
那么就肯定會(huì)別可以使用這樣的無(wú)比嗎?不過(guò)也不是,在是真的有這樣的需求的時(shí)候,我們可以這樣的在用,只是千萬(wàn)記住,不要把它真有當(dāng)作完全控制流程的工具或手段。這樣也不知什么時(shí)候才要丟出這樣的極其呢?要考慮到,如果不是動(dòng)態(tài)創(chuàng)建者調(diào)用程序出錯(cuò)后,一定得讓動(dòng)態(tài)鏈接庫(kù)者對(duì)此錯(cuò)誤通過(guò)處理才是可以,柯西-黎曼方程這樣的要求時(shí),我們才能夠考慮使用受檢異樣。
接下來(lái),我們來(lái)看一下非受檢十分呢(RuntimeException),相對(duì)于RuntimeException這種極其,我們當(dāng)然很多見,比如說(shuō)/等,這樣的話這種無(wú)比我們時(shí)候擲下呢?
當(dāng)我們?cè)趯懩硞€(gè)方法的時(shí)候,肯定會(huì)偶然路過(guò)遇到某個(gè)錯(cuò)誤,我們其實(shí)這個(gè)問(wèn)題時(shí)不運(yùn)行時(shí)肯定為發(fā)生的,而且理論上講,沒(méi)有這個(gè)問(wèn)題的話,程序?qū)⑹嵌颊O雸?zhí)行的時(shí)候,它不噬靈鬼斬具體的要求動(dòng)態(tài)鏈接庫(kù)者一定得能捕捉這個(gè)異常,此時(shí)拋出RuntimeException十分。
舉個(gè)例子,當(dāng)傳來(lái)一個(gè)路徑的時(shí)候,需要回一個(gè)路徑填寫的File對(duì)象:
上述事項(xiàng)例子是因?yàn)?,如果全局函?shù)者內(nèi)部函數(shù)getFiles(String)的時(shí)候如果不是path是空,那就就拋出空指針極其(它是RuntimeException的子類),全局函數(shù)者不用什么顯示的接受try…catch…操作進(jìn)行強(qiáng)制破軍處理.這就要求全局函數(shù)者在調(diào)用這樣的方法時(shí)先進(jìn)行驗(yàn)證,盡量減少再一次發(fā)生RuntimeException.追加:
估計(jì)最好選擇哪種極其
實(shí)際以上的描述和舉例說(shuō)明,可以總結(jié)歸納出一個(gè)結(jié)論,RuntimeException異常和受檢無(wú)比之間的區(qū)別應(yīng)該是:是否是強(qiáng)制具體的要求調(diào)用者需要去處理此異常,要是噬魂之手具體的要求調(diào)用者要進(jìn)行處理,這樣的話就可以使用受檢極其,不然就選擇類型非受檢無(wú)比(RuntimeException)。好象來(lái)講,假如沒(méi)有特殊的方法的要求,我們我建議你使用RuntimeException十分。
場(chǎng)景推薦和技術(shù)選型架構(gòu)描述
很顯然我們所知,比較傳統(tǒng)的項(xiàng)目全是以MVC框架為基礎(chǔ)接受開發(fā)的,本文主要從建議使用restful風(fēng)格接口的設(shè)計(jì)來(lái)可以體驗(yàn)幫一下忙異常處理的優(yōu)雅。
我們把關(guān)注點(diǎn)放在restful的api層(和web中的controller層帶有)和service層,想研究一下在service中如何一拋十分,后再api層如何通過(guò)捕捉獵物因此轉(zhuǎn)化異常。
建議使用的技術(shù)是:spring-boot,jpa(hibernate),mysql,如果不是對(duì)這些技術(shù)又不是太熟得不能再熟,讀者必須一一泛讀相關(guān)材料。
業(yè)務(wù)場(chǎng)景描述
選擇一個(gè)也很最簡(jiǎn)單業(yè)務(wù)場(chǎng)景,以電商中的收貨地址管理為例,用戶在移動(dòng)端參與購(gòu)買商品時(shí),要并且收貨地址管理,在項(xiàng)目中,提供給一些給移動(dòng)端進(jìn)行訪問(wèn)的api接口,如:直接添加收貨地址,刪除收貨地址,更改后收貨地址,默認(rèn)收貨地址設(shè)置,收貨地址列表可以查詢,單個(gè)確認(rèn)發(fā)貨地址查詢等接口。
統(tǒng)合約束條件
可以啦,這個(gè)是設(shè)置好的一個(gè)很基本的業(yè)務(wù)場(chǎng)景,肯定,無(wú)論什么樣的api操作,其中都中有一些規(guī)則:
添加收貨地址:入?yún)?
用戶id
收貨地址實(shí)體信息
加以約束:
用戶id不能為空,且此用戶雖然是修真者的存在的
收貨地址的沒(méi)有必要字段沒(méi)法為空
假如用戶還沒(méi)有收貨地址,當(dāng)此收貨地址創(chuàng)建時(shí)設(shè)置里成默認(rèn)收貨地址—
刪除收貨地址:入?yún)?
用戶id
收貨地址id
強(qiáng)制力:
用戶id沒(méi)法為空,且此用戶倒是是未知的
收貨地址又不能為空,且此收貨地址倒是是存在地的
確認(rèn)此收貨地址是否是用戶的收貨地址
可以確定此收貨地址是否需要為默認(rèn)收貨地址,要是是設(shè)置成收貨地址,這樣的話又不能通過(guò)刪掉
你要改收貨地址:入?yún)?
用戶id
收貨地址id
約束:
用戶id不能不能為空,且此用戶的確是存在的
收貨地址沒(méi)法為空,且此收到貨地址倒是是存在的
推測(cè)此收貨地址如何確定是用戶的收貨地址
設(shè)置成地址設(shè)置:入?yún)?
用戶id
收貨地址id
管理和約束:
用戶id不能為空,且此用戶確實(shí)是是修真者的存在的
收貨地址不能為空,且此收貨地址雖然是修真者的存在的
可以確定此收貨地址是否是是用戶的收貨地址
收貨地址列表網(wǎng)上查詢:入?yún)?
用戶id
約束力:
用戶id不能不能為空,且此用戶倒是是未知的
單個(gè)收貨地址查詢:入?yún)?
用戶id
收貨地址id
強(qiáng)制力:
用戶id不能不能為空,且此用戶雖然是存在的
收貨地址不能為空,且此收貨地址確實(shí)是是未知的
判斷此收貨地址是否是用戶的收貨地址
強(qiáng)制力判斷和技術(shù)選型
對(duì)此根據(jù)上述規(guī)定列下的約束條件和功能列表,我選擇幾個(gè)都很典型的異常處理場(chǎng)景并且分析:直接添加收貨地址,徹底刪除收貨地址,資源收貨地址列表。
這樣的話應(yīng)該要有哪些必要的知識(shí)儲(chǔ)備呢,讓我們查查收貨地址這個(gè)功能:
再添加收貨地址中必須對(duì)用戶id和收貨地址實(shí)體信息就行校驗(yàn),那就相對(duì)于非空的判斷,我們?cè)趺唇邮芄ぞ叩倪x擇呢?比較傳統(tǒng)的判斷如下:
上邊的例子,如果只判斷uid為空好在,要是再去判斷address這個(gè)實(shí)體中的某些必要屬性是否需要為空,在字段很多的情況下,這說(shuō)白是災(zāi)難性的。
那我們應(yīng)該要怎摸并且這些入?yún)⒌呐袛嗄?,給大家可以介紹兩個(gè)知識(shí)點(diǎn):
Guava中的Preconditions類實(shí)現(xiàn)了很多入?yún)⒎椒ǖ呐袛?/p>
jsr303的validation規(guī)范(目前實(shí)現(xiàn)程序都很全的是hibernate實(shí)現(xiàn)程序的hibernate-validator)
如果沒(méi)有使用了這兩種推薦技術(shù),那么入?yún)⒌呐袛鄷?huì)變得異常簡(jiǎn)單點(diǎn)比較多。推薦一下大家多在用這些成熟的技術(shù)和jar工具包,他可以不會(huì)減少很多不必要的工作量。我們只不需要把重心放到業(yè)務(wù)邏輯上。而應(yīng)該不會(huì)只不過(guò)這些入?yún)⒌呐袛嗟⒄`時(shí)間更多的時(shí)間。
怎么優(yōu)雅的啊,設(shè)計(jì)java極其domain詳細(xì)介紹
據(jù)項(xiàng)目場(chǎng)景來(lái)看,不需要兩個(gè)domain模型,一個(gè)是用戶實(shí)體,一個(gè)是地址實(shí)體.
Addressdomain追加:
Userdomain追加:
可以啦,上邊是一個(gè)模型關(guān)系,用戶-收貨地址的關(guān)系是1-n的關(guān)系。上邊的@Data是使用了一個(gè)就是lombok的工具,它自動(dòng)導(dǎo)入了Setter和Getter等方法,用起來(lái)非常方便,感興趣的讀者是可以無(wú)法清楚再看看。
dao可以介紹
數(shù)據(jù)連接層,我們不使用了spring-data-jpa這個(gè)框架,它具體的要求我們只要不能繼承框架提供的接口,并且通過(guò)約定對(duì)方法接受取名,就是可以能完成我們想要的數(shù)據(jù)庫(kù)操作。
用戶數(shù)據(jù)庫(kù)操作不勝感激:
收貨地址操作如下:
如果說(shuō)讀者所看見的,我們的DAO只必須能繼承JpaRepository,它就早幫我們結(jié)束了都差不多的CURD等操作,假如想知道一點(diǎn)一些跪求spring-data的這個(gè)項(xiàng)目,請(qǐng)可以參考再看看spring的官方文檔,它比不方案我們對(duì)異常的研究。
Service異樣設(shè)計(jì)
ok,再一次到了我們的重點(diǎn)了,我們要結(jié)束service一些的部分操作:直接添加收貨地址,刪出收貨地址,獲取收貨地址列表.
首先看我的service接口定義:
我們來(lái)參與看看利用:
添加收貨地址
簡(jiǎn)單以后再來(lái)看下之前收拾好的約束條件:
入?yún)?
用戶id
收貨地址實(shí)體信息
管理和約束:
用戶id沒(méi)法為空,且此用戶倒是是存在的
收貨地址的必要的話字段沒(méi)法為空
如果沒(méi)有用戶還沒(méi)有收貨地址,當(dāng)此收貨地址創(chuàng)建家族時(shí)設(shè)置里成默認(rèn)收貨地址
先看以上代碼利用:
其中,也完成了根據(jù)上述規(guī)定所詳細(xì)解釋的三點(diǎn)約束條件,當(dāng)三點(diǎn)約束條件都不滿足時(shí),才可以接受都正常的業(yè)務(wù)邏輯,不然將擲下異樣(象在此處建議您一拋運(yùn)行時(shí)十分-RuntimeException)。
能介紹以下以上我所都用到的技術(shù):
1、(T t)這個(gè)是建議使用Guava中的參與推測(cè)的,是因?yàn)閟ervice中應(yīng)用的不驗(yàn)證較低,所以我我建議你將Preconfitions把它改成靜態(tài)導(dǎo)入的
當(dāng)然了Guava的github中的只能說(shuō)明也個(gè)人建議我們那樣建議使用。
2、(validator,address)這個(gè)使用了hibernate實(shí)現(xiàn)的jsr303規(guī)范來(lái)做的,必須傳遍一個(gè)validator和一個(gè)是需要驗(yàn)正的實(shí)體,那么validator是該如何查看的呢,萬(wàn)分感謝:
他將資源一個(gè)Validator對(duì)象,然后再我們?cè)趕ervice中參與吸納便是可以可以使用了:
那么BeanValidators這個(gè)類是要如何實(shí)現(xiàn)程序的?不過(guò)實(shí)現(xiàn)很很簡(jiǎn)單,只要去可以確定jsr303的標(biāo)示注解就就ok啦了。
這樣jsr303的注解寫哪了呢?其實(shí)是寫在address實(shí)體類中了: