日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

高效的java异常(Effective Java Exceptions)

發(fā)布時(shí)間:2023/12/14 java 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 高效的java异常(Effective Java Exceptions) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

摘要

Java開發(fā)人員可以做出的最重要的架構(gòu)決策之一是如何使用Java異常模型。Java異常一直是社區(qū)爭(zhēng)論的主題。 有些人認(rèn)為Java語(yǔ)言中的checked(受檢)異常是一個(gè)失敗的實(shí)驗(yàn)。 本文認(rèn)為,錯(cuò)誤不在于Java模型,而在于Java庫(kù)設(shè)計(jì)者未能認(rèn)知到方法失敗的兩個(gè)基本原因。 本文提倡一種思考異常情形性質(zhì)的方法,并描述有助于您設(shè)計(jì)的設(shè)計(jì)模式。 最后,本文討論了異常處理作為面向切面編程模型中的橫切關(guān)注點(diǎn)。 正確使用Java異常是一個(gè)很大的好處。 本文將幫助您做到這一點(diǎn)。

為什么異常事關(guān)緊要

Java應(yīng)用程序中的異常處理可以告訴您很多用于構(gòu)建它的體系結(jié)構(gòu)的強(qiáng)大。 架構(gòu)是關(guān)于在應(yīng)用程序的所有級(jí)別上一致地做出和遵循的決定。 要做出的最重要的決定之一是應(yīng)用程序中的類,子系統(tǒng)或應(yīng)用內(nèi)的各個(gè)層的相互通信的方式。 Java異常是方法傳遞操作的替代結(jié)果的方式,因此在你的應(yīng)用體系結(jié)構(gòu)中值得特別關(guān)注。

衡量Java架構(gòu)師技能和開發(fā)團(tuán)隊(duì)紀(jì)律的一個(gè)好方法是查看其應(yīng)用程序中的異常處理代碼。 首先要注意的是,有多少代碼用于捕獲異常,記錄異常,嘗試確定發(fā)生了什么情況以及將一個(gè)異常轉(zhuǎn)換為另一個(gè)異常。 清晰,緊湊和一致的(coherent)異常處理表明團(tuán)隊(duì)具有一致的使用Java異常的方法。 當(dāng)異常處理代碼的數(shù)量可能超過(guò)其他所有代碼時(shí),您可以判斷團(tuán)隊(duì)成員之間的交流已經(jīng)崩潰(或者從一開始就不存在),并且每個(gè)人都以“他們自己的方式”處理異常。

臨時(shí)異常處理的結(jié)果是非常容易預(yù)測(cè)的。 如果你問(wèn)團(tuán)隊(duì)成員他們?yōu)槭裁丛谒麄兊拇a中的特定地方拋出、捕獲或忽略異常,回答通常是“我不知道還能做什么”。 如果你問(wèn)他們?nèi)绻麄兇a中的異常確實(shí)發(fā)生了會(huì)怎樣,那么他們就會(huì)皺眉頭了,你會(huì)得到一個(gè)類似于“我不知道。我們從未測(cè)試過(guò)它”的回答。

你可以通過(guò)查看Java組件的客戶端(client)代碼來(lái)判斷它是否有效地使用了Java異常。 如果它們包含大量邏輯以確定操作何時(shí)失敗,為什么失敗,原因幾乎總是因?yàn)榻M件的錯(cuò)誤報(bào)告設(shè)計(jì)(error reporting design)有問(wèn)題。 有缺陷的報(bào)告在客戶端產(chǎn)生大量“記錄和遺忘”(log and forget)代碼,很少有用。 最糟糕的是扭曲的邏輯路徑,嵌套的try/catch/finally塊,以及其它導(dǎo)致應(yīng)用程序脆弱且難以管理的混亂。

將異常作為事后補(bǔ)救(或根本不解決它們)是導(dǎo)致軟件項(xiàng)目混亂和延期的主要原因。 異常處理是一個(gè)涉及設(shè)計(jì)的所有部分的問(wèn)題。 建立異常的架構(gòu)約定應(yīng)該是項(xiàng)目需要做的第一個(gè)決策。 正確地使用Java異常模型將大大有助于保持應(yīng)用程序的簡(jiǎn)單性,可維護(hù)性和正確性。

挑戰(zhàn)異常標(biāo)準(zhǔn)

正確使用Java異常模型需要怎么做一直是爭(zhēng)論的主題。 Java不是第一種支持異常語(yǔ)義的編程語(yǔ)言; 但是,它是編譯器強(qiáng)制執(zhí)行規(guī)則以管理某些異常的聲明和處理的第一種語(yǔ)言。 許多人認(rèn)為編譯時(shí)異常檢查有助于精確的軟件設(shè)計(jì),與其他語(yǔ)言特性很好地協(xié)調(diào)工作。 圖1顯示了Java異常層次結(jié)構(gòu)。

通常,Java編譯器會(huì)強(qiáng)制一個(gè)拋出基于java.lang.Throwable的異常的方法,在方法聲明中有“throws”子句。 此外,編譯器驗(yàn)證方法的客戶端是捕獲聲明的異常類型還是指定它們自己拋出該異常類型。 這些簡(jiǎn)單的規(guī)則對(duì)全世界的Java開發(fā)人員產(chǎn)生了深遠(yuǎn)的影響。

編譯器將Throwable繼承樹的兩個(gè)分支的異常檢查行為分開處理。 java.lang.Error和java.lang.RuntimeException的子類編譯時(shí)不用檢查。 在這兩者中,軟件設(shè)計(jì)者通常對(duì)運(yùn)行時(shí)異常更感興趣。 術(shù)語(yǔ)“未經(jīng)檢查”(unchecked)的異常即指運(yùn)行時(shí)異常,它們區(qū)別于所有其它“已檢查”(checked)的異常。

我想,那些同樣重視Java強(qiáng)類型的人也接受了checked異常。畢竟,編譯器對(duì)數(shù)據(jù)類型施加的約束是對(duì)嚴(yán)格的編碼和精確的思考的一種鼓勵(lì)。編譯時(shí)類型檢查有助于防止在運(yùn)行時(shí)出現(xiàn)令人討厭的意外情況。編譯時(shí)異常檢查的工作方式類似,提醒開發(fā)人員方法具有需要解決的潛在的其他結(jié)果。

在早期,建議盡可能使用已檢查的異常,以最大限度地利用編譯器提供的幫助來(lái)生產(chǎn)無(wú)錯(cuò)誤的軟件。 Java庫(kù)的API設(shè)計(jì)者顯然訂閱了已檢查的異常標(biāo)準(zhǔn),使用這些異常來(lái)廣泛地模擬庫(kù)方法中可能發(fā)生的任何意外事件。在J2SE 5.1 API規(guī)范中,已檢查的異常類型仍然超過(guò)未經(jīng)檢查的類型的兩倍以上。

對(duì)于程序員來(lái)說(shuō),似乎Java庫(kù)類中的大多數(shù)常用方法都會(huì)為每個(gè)可能的失敗聲明checked異常。例如,java.io包嚴(yán)重依賴于checked異常IOException。至少有63個(gè)Java庫(kù)包直接或通過(guò)其中的幾個(gè)子類之一拋出此異常。

I/O失敗是一個(gè)嚴(yán)重但非常罕見(jiàn)的事件。最重要的是,您的代碼通常無(wú)法從一個(gè)IO失敗中恢復(fù)。 Java程序員發(fā)現(xiàn)自己在簡(jiǎn)單的Java庫(kù)方法調(diào)用中被迫提供IOException和類似的不可恢復(fù)的事件。捕獲這些異常會(huì)給原本簡(jiǎn)單的代碼增加混亂,因?yàn)樵赾atch塊中幾乎做不了什么事情來(lái)幫助解決這個(gè)問(wèn)題。沒(méi)有捕獲它們可能更糟,因?yàn)榫幾g器要求你將它們添加到方法拋出的異常列表中。這暴露了良好的面向?qū)ο笤O(shè)計(jì)自然的應(yīng)該隱藏的實(shí)現(xiàn)細(xì)節(jié)。

這種沒(méi)有贏家的局面導(dǎo)致了大多數(shù)現(xiàn)在我們警告的臭名昭著的反模式異常處理。它還以正確的和錯(cuò)誤的方法提出了許多建議來(lái)構(gòu)建變通方法。

一些Java名人開始質(zhì)疑Java的checked異常模型是否是一個(gè)失敗的實(shí)驗(yàn)。有些事情確實(shí)失敗了,但它與在Java語(yǔ)言中包含異常檢查無(wú)關(guān)。失敗的原因在于Java API設(shè)計(jì)者認(rèn)為大多數(shù)失敗情況都是相同的,并且可以通過(guò)相同的異常進(jìn)行傳達(dá)。

故障和意外(Faults and Contingencies)

考慮一個(gè)虛構(gòu)的銀行應(yīng)用程序中的CheckingAccount類。 CheckingAccount屬于客戶,維護(hù)當(dāng)前余額,并且能夠接受存款,接受支票上的止付訂單以及處理收到的支票。 CheckingAccount對(duì)象必須協(xié)調(diào)并發(fā)線程的訪問(wèn),其中任何一個(gè)線程都可能改變其狀態(tài)。CheckingAccount的processCheck()方法接受Check對(duì)象作為參數(shù),通常從帳戶余額中扣除支票金額。但是調(diào)用processCheck()的檢查清除客戶端必須準(zhǔn)備好應(yīng)對(duì)兩個(gè)意外事件。首先,CheckingAccount可能有一個(gè)為支票注冊(cè)的止付訂單。其次,賬戶可能沒(méi)有足夠的資金來(lái)支付支票金額。

因此,processCheck()方法可以以三種可能的方式響應(yīng)其調(diào)用者。正常的響應(yīng)是檢查得到處理,方法簽名中聲明的結(jié)果返回給調(diào)用服務(wù)。這兩個(gè)意外響應(yīng)代表了非常真實(shí)的銀行領(lǐng)域中需要傳達(dá)給支票清算客戶的情況。所有三個(gè)processCheck()響應(yīng)都是有意設(shè)計(jì)的,用于模擬典型支票賬戶的行為。

在Java中表示意外響應(yīng)的自然方式是定義兩個(gè)異常,比如StopPaymentException和InsufficientFundsException。客戶端忽略這些是不對(duì)的,因?yàn)樗鼈兛隙〞?huì)被拋入應(yīng)用程序的正常操作中。它們有助于表達(dá)方法的完整行為,與方法簽名一樣重要。

客戶端可以輕松處理這兩種異常。如果支票上的付款被停止,客戶端可以將支票路由到特殊處理的邏輯。如果資金不足,客戶端可以從客戶的儲(chǔ)蓄賬戶轉(zhuǎn)移資金以支付支票,然后再試一次。

意外事件以及使用CheckingAccount API的正常流程都被預(yù)測(cè)了。它們不代表軟件或運(yùn)行環(huán)境的故障。將這些與由于與CheckingAccount類的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)相關(guān)的問(wèn)題而可能出現(xiàn)的真正的失敗進(jìn)行對(duì)比。

想象一下,CheckingAccount在數(shù)據(jù)庫(kù)中維護(hù)其持久狀態(tài),并使用JDBC API來(lái)訪問(wèn)它。由于與CheckingAccount的實(shí)現(xiàn)無(wú)關(guān)的原因,該API中幾乎每種數(shù)據(jù)庫(kù)訪問(wèn)方法都有可能失敗。例如,有人可能忘記打開數(shù)據(jù)庫(kù)服務(wù)器,拔掉網(wǎng)絡(luò)電纜或更改訪問(wèn)數(shù)據(jù)庫(kù)所需的密碼。

JDBC依賴于單個(gè)checked異常SQLException來(lái)報(bào)告可能出錯(cuò)的所有內(nèi)容。大多數(shù)可能出錯(cuò)的地方都與配置數(shù)據(jù)庫(kù),連接數(shù)據(jù)庫(kù)及其所在的硬件有關(guān)。processCheck()方法無(wú)法以有意義的方式處理這些情況。processCheck()至少知道它自己的實(shí)現(xiàn)。調(diào)用堆棧中的上游方法具有更小的解決問(wèn)題的可能性。

CheckingAccount示例說(shuō)明了方法執(zhí)行無(wú)法返回其預(yù)期結(jié)果的兩個(gè)基本原因。它們值得一些描述性術(shù)語(yǔ):

意外(Contingency)

方法可以用拋出異常的方式表達(dá)可能的異常(方法罕見(jiàn)的結(jié)果,但是是方法可能的返回值,只是不太常見(jiàn),比如賬戶余額不足異常)。 該方法的調(diào)用者具有應(yīng)對(duì)它們的策略。

故障(Fault)

一種出乎意料的情況,使得方法不能返回預(yù)期的值,如果不參考方法的內(nèi)部實(shí)現(xiàn),則無(wú)法對(duì)其進(jìn)行描述。

使用此術(shù)語(yǔ),停止付款訂單和透支是processCheck()方法的兩種可能的意外(Contingency)情況。 SQL問(wèn)題表示可能的故障(Fault)情況。 processCheck()的調(diào)用者應(yīng)該有一種方法來(lái)處理意外(Contigency)事件,但如果發(fā)生故障(Fault)這種情況,則無(wú)法合理地預(yù)期處理它。

匹配Java異常

在意外和故障方面考慮“可能出現(xiàn)的問(wèn)題”將大大有助于在應(yīng)用程序架構(gòu)中建立Java異常約定。

情況(Condition)意外(Contingency)故障(Fault)
被認(rèn)為是設(shè)計(jì)中的一部分令人厭惡的"驚喜"
是否被期望發(fā)生希望很少發(fā)生希望永不發(fā)生
誰(shuí)關(guān)注它調(diào)用方法的上游方法需要處理問(wèn)題的人
例子替代返回模式(Alternative return modes)程序bug、硬件故障、配置錯(cuò)誤、缺少文件、服務(wù)器訪問(wèn)不了
最佳匹配checked異常unchecked異常

意外情況很好地映射到Java的checked異常。由于它們是方法語(yǔ)義契約的組成部分,因此有必要使用編譯器的幫助來(lái)確保它們得到解決。如果你發(fā)現(xiàn)編譯器強(qiáng)制你在不方便的情況下處理或聲明異常,那么你的設(shè)計(jì)需要重構(gòu)一下了。這實(shí)際上是件好事。

人們對(duì)故障情況(Fault conditions)很感興趣,但是軟件邏輯卻不然。那些扮演“軟件直腸病學(xué)家”角色的人需要有關(guān)故障的信息來(lái)診斷和修復(fù)導(dǎo)致它們發(fā)生的任何事情。因此,未經(jīng)檢查的Java異常是表示故障的完美方式。它們?cè)试S故障通知通過(guò)調(diào)用堆棧上的所有方法不受影響地滲透到專門設(shè)計(jì)用于捕獲它們的層面(level),捕獲它們包含的診斷信息,并為程序活動(dòng)提供受控且優(yōu)雅的結(jié)論。能夠產(chǎn)生故障的方法不需要在方法上聲明它,不需要上游方法來(lái)捕獲它們,并且方法的實(shí)現(xiàn)保持適當(dāng)隱藏 - 所有這些都具有最少的代碼混亂。

較新的Java API(如Spring Framework和Java Data Objects庫(kù))很少或根本不依賴于受檢(checked)異常。 Hibernate ORM框架從3.0版開始重新定義了主要部分,以消除checked異常的使用。這反映了這樣的認(rèn)識(shí),即這些框架所報(bào)告的絕大多數(shù)異常情況都是不可恢復(fù)的,這些異常情況源于方法調(diào)用的錯(cuò)誤編碼或某些底層組件(如數(shù)據(jù)庫(kù)服務(wù)器)的故障。實(shí)際上,通過(guò)強(qiáng)制調(diào)用者捕獲或聲明此類異常幾乎沒(méi)有任何好處。

在你的架構(gòu)中處理故障

在您的架構(gòu)中有效處理故障的第一步是承認(rèn)你需要這樣做。 對(duì)于那些以創(chuàng)造無(wú)可挑剔的軟件能力而自豪的工程師而言,很難接受這種認(rèn)可。 下面是一些有幫助的理由。 首先,你的應(yīng)用程序?qū)⒒ㄙM(fèi)大量時(shí)間進(jìn)行開發(fā),其中錯(cuò)誤是司空見(jiàn)慣的。 提供程序員造成的故障將使你的團(tuán)隊(duì)更容易診斷和修復(fù)它們。 其次,在Java庫(kù)中(過(guò)度)使用checked異常來(lái)處理故障情況將迫使你的代碼處理它們,即使你的調(diào)用順序完全正確。 如果沒(méi)有適當(dāng)?shù)墓收咸幚砜蚣?#xff0c;臨時(shí)異常處理會(huì)將熵注入你的應(yīng)用程序。

一個(gè)成功的錯(cuò)誤處理框架需要完成四個(gè)目標(biāo):

  • 最小化代碼混亂
  • 捕獲錯(cuò)誤并且保存診斷信息
  • 提醒正確的人
  • 優(yōu)雅地退出程序

錯(cuò)誤會(huì)分散應(yīng)用程序的真實(shí)目的。 因此,用于處理它們的代碼量應(yīng)該是最小的,并且理想地,與應(yīng)用程序的語(yǔ)義部分隔離。 故障處理必須滿足負(fù)責(zé)糾正它們的人的需要。 他們需要知道發(fā)生了故障,并獲得有助于他們弄清楚原因的信息。 即使根據(jù)定義,故障不可恢復(fù),良好的故障處理也會(huì)嘗試以優(yōu)雅的方式終止遇到故障的活動(dòng)。

為故障情況使用unchecked異常

有許多理由使架構(gòu)決策用unchecked異常來(lái)表示故障情況。 Java運(yùn)行時(shí)通過(guò)拋出RuntimeException子類(如ArithmeticException和ClassCastException)來(lái)"獎(jiǎng)勵(lì)"編程錯(cuò)誤,為你的體系結(jié)構(gòu)設(shè)置先例。unchecked異常通過(guò)讓上游方法不用處理和它意圖無(wú)關(guān)的錯(cuò)誤而使得代碼更整潔。

你的故障處理策略應(yīng)該認(rèn)識(shí)到Java庫(kù)和其他API中的方法可能使用checked異常來(lái)表示應(yīng)用程序上下文中的故障條件。在這種情況下,采用體系結(jié)構(gòu)約定來(lái)捕獲發(fā)生的API異常,將其視為故障,并拋出unchecked異常以發(fā)出故障信號(hào)并捕獲診斷信息。

在這種情況下拋出的特定異常類型應(yīng)該由你的體系結(jié)構(gòu)定義。不要忘記,故障異常的主要目的是傳達(dá)將被記錄的診斷信息,以幫助人們找出問(wèn)題所在。使用多個(gè)故障異常類型可能有點(diǎn)過(guò)分,因?yàn)槟愕捏w系結(jié)構(gòu)將完全相同地處理它們。嵌入在單個(gè)故障異常類型中的良好描述性消息將在大多數(shù)情況下完成工作。使用Java的通用RuntimeException來(lái)表示你的故障情況很容易。從Java 1.4開始,RuntimeException與所有throwable一樣,支持異常鏈,允許你捕獲并報(bào)告引發(fā)故障的checked異常。

你可以選擇為故障報(bào)告定義自己的unchecked異常。如果你需要使用不支持異常鏈的Java 1.3或更早版本,則必須執(zhí)行此操作。實(shí)現(xiàn)類似的鏈功能可以很容易地捕獲和轉(zhuǎn)換構(gòu)成應(yīng)用程序故障的checked異常。你的應(yīng)用程序可能需要在故障報(bào)告異常中執(zhí)行特殊操作。這將是為你的體系結(jié)構(gòu)創(chuàng)建RuntimeException子類的另一個(gè)原因。

建立一個(gè)故障屏障(Establish a fault barrier)

決定拋出哪個(gè)異常以及何時(shí)拋出它是你的故障處理框架的重要決策。關(guān)于何時(shí)捕獲故障異常以及之后要做什么的問(wèn)題同樣重要。這里的目標(biāo)是使應(yīng)用程序的功能部分免于處理故障的責(zé)任。關(guān)注點(diǎn)分離通常是件好事,負(fù)責(zé)處理故障的中央設(shè)施將在未來(lái)發(fā)揮作用。

在故障屏障模式中,任何應(yīng)用程序組件都可以引發(fā)故障異常,但只有充當(dāng)“故障屏障”的組件才能捕獲它們。采用這種模式消除了開發(fā)人員在本地插入處理故障的大量復(fù)雜代碼。故障屏障邏輯上位于調(diào)用堆棧的頂部,在觸發(fā)默認(rèn)操作之前它停止向上傳播異常。默認(rèn)操作根據(jù)應(yīng)用程序類型的不同而不同。對(duì)于獨(dú)立Java應(yīng)用程序,這意味著活動(dòng)線程終止。對(duì)于由應(yīng)用程序服務(wù)器托管的Web應(yīng)用程序,這意味著應(yīng)用程序服務(wù)器向?yàn)g覽器發(fā)送不友好(且令人尷尬)的響應(yīng)。

故障屏障組件的第一個(gè)職責(zé)是記錄故障異常中包含的信息,以便將來(lái)采取措施。到目前為止,應(yīng)用程序日志是執(zhí)行此操作的最佳位置。異常的鏈接消息,堆棧跟蹤等對(duì)于診斷人員來(lái)說(shuō)都是有價(jià)值的信息。發(fā)送故障信息的最差位置是跨用戶界面。讓你的應(yīng)用程序的客戶端參與調(diào)試過(guò)程對(duì)你或你的客戶來(lái)說(shuō)幾乎沒(méi)有任何好處。如果你真的想要用診斷信息繪制用戶界面,則可能意味著你的日志記錄策略需要改進(jìn)。

故障屏障的下一個(gè)責(zé)任是以受控方式關(guān)閉操作。這意味著什么取決于你的應(yīng)用程序設(shè)計(jì),但通常涉及生成對(duì)可能正在等待的客戶端的通用響應(yīng)。如果應(yīng)用程序是Web服務(wù),則意味著使用soap:Server的<faultcode>和通用<faultstring>失敗消息在響應(yīng)中構(gòu)建SOAP <fault>元素。如果應(yīng)用程序與Web瀏覽器通信,屏障將安排發(fā)送通用HTML響應(yīng),指示無(wú)法處理請(qǐng)求。

在Struts應(yīng)用程序中,你的故障屏障可以采用全局異常處理器的形式,該處理器被配置為處理RuntimeException的任何子類。你的故障屏障類將擴(kuò)展org.apache.struts.action.ExceptionHandler,根據(jù)需要覆蓋方法以實(shí)現(xiàn)你需要的自定義處理。這將處理在處理Struts操作期間明顯發(fā)現(xiàn)的無(wú)意生成的故障條件和故障情況。圖2顯示了意外事件和故障異常。

如果您正在使用Spring MVC框架,則可以通過(guò)擴(kuò)展SimpleMappingExceptionResolver并將其配置為處理RuntimeException及其子類來(lái)輕松構(gòu)建故障屏障。 通過(guò)重寫resolveException()方法,你可以在使用超類方法將請(qǐng)求路由到發(fā)送通用錯(cuò)誤顯示的視圖之前添加所需的任何自定義處理。

當(dāng)你的架構(gòu)包含故障屏障并且開發(fā)人員意識(shí)到它時(shí),編寫一次性故障異常處理代碼的誘惑就會(huì)大大減少。 結(jié)果是整個(gè)應(yīng)用程序中的代碼更清晰,更易于維護(hù)。

你架構(gòu)中的意外處理(Contingency Handling in Your Architecture)

隨著故障處理降級(jí)到屏障,主要組件之間的應(yīng)急通信變得更加簡(jiǎn)單。意外事件代表一種替代方法結(jié)果,與主要的返回結(jié)果同樣重要。因此,checked異常類型是傳達(dá)意外情況存在的良好工具,并提供處理意外所需的信息。這種做法需要Java編譯器的幫助,以提醒開發(fā)人員他們正在使用的API的所有方面以及需要提供所有方法結(jié)果。

通過(guò)單獨(dú)使用方法的返回類型可以傳達(dá)簡(jiǎn)單的意外事件。例如,返回空引用而不是實(shí)際對(duì)象可以表示因?yàn)槟撤N原因無(wú)法創(chuàng)建對(duì)象。 Java I/O方法通常返回整數(shù)值-1,而不是字節(jié)值或字節(jié)數(shù),以表示文件結(jié)束的情況。如果你的方法的語(yǔ)義足夠簡(jiǎn)單,那么替代返回值可能是最好的選擇,因?yàn)樗鼈兿水惓?lái)的開銷。缺點(diǎn)是方法調(diào)用者負(fù)責(zé)測(cè)試返回值以查看它是主要結(jié)果還是偶然結(jié)果。但是,編譯器不會(huì)強(qiáng)制方法調(diào)用者進(jìn)行該測(cè)試。

如果方法具有void返回類型,則異常是表示發(fā)生意外事件的唯一方法。如果方法返回對(duì)象引用,則返回值可以表達(dá)的詞匯表限制為兩個(gè)值(null和non-null)。如果方法返回一個(gè)整數(shù)值,則可以通過(guò)選擇保證不與主要返回值沖突的值來(lái)表達(dá)幾個(gè)意外情況。但現(xiàn)在我們已經(jīng)進(jìn)入了錯(cuò)誤代碼檢查的世界,開發(fā)了Java異常模型以避免這種情況。

提供一些有用的東西

定義不同的故障(fault)報(bào)告異常類型沒(méi)有多大意義,因?yàn)楣收掀琳蠈?duì)它們的處理方式完全相同。 意外(contingency)異常是完全不同的,因?yàn)樗鼈冎荚谙蚍椒ㄕ{(diào)用者傳達(dá)不同的條件,你的體系結(jié)構(gòu)可能會(huì)指定這些異常(指的是contingency exception)應(yīng)該擴(kuò)展java.lang.Exception或指定的基類。

不要忘記你的異常是完整的Java類型,它們可以包含專門的字段,方法,甚至可以為你的獨(dú)特目的而設(shè)計(jì)的構(gòu)造函數(shù)。 例如,假想的CheckingAccount processCheck()方法拋出的InsufficientFundsException類型可以包含一個(gè)OverdraftProtection對(duì)象,該對(duì)象能夠幫助轉(zhuǎn)入所需的資金以彌補(bǔ)賬戶余額的不足。

記錄還是不記錄日志

記錄故障(fault)異常是有意義的,因?yàn)樗鼈兊哪康氖且鹑藗冏⒁庑枰m正的情況。 對(duì)于意外(contingency)異常,則不能這么說(shuō),因?yàn)樗鼈兛赡艽硐鄬?duì)罕見(jiàn)的事件,但預(yù)計(jì)它們中的每一個(gè)都會(huì)在你的應(yīng)用程序生命周期中發(fā)生。 如果有的話,它們僅僅表示應(yīng)用程序的運(yùn)行方式與其工作方式相同罷了。 將記錄代碼添加到意外(contingency)捕獲塊會(huì)增加代碼的混亂,而沒(méi)有實(shí)際的好處。 如果意外事件代表重大事件,則在拋出意外事件異常以警告其調(diào)用者之前,生成記錄事件的日志的方法可能更好。

異常切面

在面向切面(也叫方面)編程(AOP)術(shù)語(yǔ)中,故障(fault)和意外(contingency)處理是橫切關(guān)注的問(wèn)題。 例如,要實(shí)現(xiàn)故障屏障模式,所有參與的類必須遵循常見(jiàn)的約定:

  • 故障屏障方法必須位于遍歷參與類的方法調(diào)用圖的頭部。
  • 它們都必須使用unchecked異常來(lái)表示故障(fault)情況。
  • 它們必須全部使用故障屏障期望接收的特定unchecked異常類型。
  • 它們都必須從被認(rèn)為是執(zhí)行上下文中的錯(cuò)誤發(fā)生的較低層方法中捕獲并轉(zhuǎn)換checked異常。
  • 它們不得干擾故障異常在通往屏障的途中傳播。

這些擔(dān)憂跨越了其他無(wú)關(guān)類的界限。結(jié)果是一小部分分散的故障處理代碼和屏障類與參與者之間的隱式耦合(盡管仍然比完全不使用模式有很大改進(jìn)!)。AOP允許將故障處理問(wèn)題封裝在應(yīng)用于參與類的公共Aspect中。諸如AspectJ和Spring AOP之類的Java AOP框架將異常處理識(shí)別為可以附加故障處理行為(或建議)的連接點(diǎn)。以這種方式,可以放寬綁定故障屏障模式中的參與者的約定。故障處理現(xiàn)在可以駐留在獨(dú)立的外部方面,從而無(wú)需將“屏障”方法放置在方法調(diào)用序列的頭部。

如果你在架構(gòu)中使用AOP,則故障(fault)和意外(contingency)處理是適用于整個(gè)應(yīng)用程序的方面的理想候選者。全面探討故障和意外處理如何在AOP世界中發(fā)揮作用將成為未來(lái)文章的一個(gè)有趣話題。

結(jié)論

雖然Java異常模型在其生命周期中產(chǎn)生了激烈的討論,但它在被正確應(yīng)用時(shí)提供了極大的價(jià)值。 作為架構(gòu)師,你需要建立從模型中獲得最大收益的約定。 在故障和意外事件方面考慮例外可以幫助你做出正確的選擇。 正確使用Java異常模型將使你的應(yīng)用程序簡(jiǎn)單,可維護(hù)和正確。 面向切面編程技術(shù)可以通過(guò)將故障和意外處理識(shí)別為橫切關(guān)注點(diǎn)來(lái)為你的架構(gòu)提供一些明確的優(yōu)勢(shì)。

原文鏈接:https://www.oracle.com/technetwork/java/effective-exceptions-092345.html

總結(jié)

以上是生活随笔為你收集整理的高效的java异常(Effective Java Exceptions)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。