欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java有效處理異常的三個(gè)原則

 更新時(shí)間:2016年09月18日 14:45:26   作者:ImportNew - 鄭瑋  
Java中異常提供了一種識(shí)別及響應(yīng)錯(cuò)誤情況的一致性機(jī)制,有效地異常處理能使程序更加健壯、易于調(diào)試。那么這篇文章總結(jié)了Java有效處理異常的三個(gè)原則,有需要的朋友們可以參考借鑒。

異常之所以是一種強(qiáng)大的調(diào)試手段,在于其回答了以下三個(gè)問(wèn)題:

     1、什么出了錯(cuò)?

     2、在哪出的錯(cuò)?

     3、為什么出錯(cuò)?

在有效使用異常的情況下,異常類(lèi)型回答了“什么”被拋出,異常堆棧跟蹤回答了“在哪“拋出,異常信息回答了“為什么“會(huì)拋出,如果你的異常沒(méi)有回答以上全部問(wèn)題,那么可能你沒(méi)有很好地使用它們。

有三個(gè)原則可以幫助你在調(diào)試過(guò)程中最大限度地使用好異常,這三個(gè)原則是:

     1、具體明確

     2、提早拋出

     3、延遲捕獲

為了闡述有效異常處理的這三個(gè)原則,本文通過(guò)杜撰個(gè)人財(cái)務(wù)管理器類(lèi)JCheckbook進(jìn)行討論,JCheckbook用于記錄及追蹤諸如存取款,票據(jù)開(kāi)具之類(lèi)的銀行賬戶(hù)活動(dòng)。

具體明確

Java定義了一個(gè)異常類(lèi)的層次結(jié)構(gòu),其以Throwable開(kāi)始,擴(kuò)展出Error和Exception,而Exception又?jǐn)U展出RuntimeException.如圖1所示.

這四個(gè)類(lèi)是泛化的,并不提供多少出錯(cuò)信息,雖然實(shí)例化這幾個(gè)類(lèi)是語(yǔ)法上合法的(如:new Throwable()),但是最好還是把它們當(dāng)虛基類(lèi)看,使用它們更加特化的子類(lèi)。Java已經(jīng)提供了大量異常子類(lèi),如需更加具體,你也可以定義自己的異常類(lèi)。

例 如:java.io package包中定義了Exception類(lèi)的子類(lèi)IOException,更加特化確的是 FileNotFoundException,EOFException和ObjectStreamException這些IOException的子 類(lèi)。每一種都描述了一類(lèi)特定的I/O錯(cuò)誤:分別是文件丟失,異常文件結(jié)尾和錯(cuò)誤的序列化對(duì)象流.異常越具體,我們的程序就能更好地回答”什么出了錯(cuò)”這個(gè) 問(wèn)題。

捕 獲異常時(shí)盡量明確也很重要。例如:JCheckbook可以通過(guò)重新詢(xún)問(wèn)用戶(hù)文件名來(lái)處理FileNotFoundException,對(duì)于 EOFException,它可以根據(jù)異常拋出前讀取的信息繼續(xù)運(yùn)行。如果拋出的是ObjectStreamException,則程序應(yīng)該提示用戶(hù)文件 已損壞,應(yīng)當(dāng)使用備份文件或者其他文件。

Java讓明確捕獲異常變得容易,因?yàn)槲覀兛梢詫?duì)同一try塊定義多個(gè)catch塊,從而對(duì)每種異常分別進(jìn)行恰當(dāng)?shù)奶幚怼?/p>

File prefsFile = new File(prefsFilename);
 
try{
  readPreferences(prefsFile);
}
catch (FileNotFoundException e){
  // alert the user that the specified file
  // does not exist
}
catch (EOFException e){
  // alert the user that the end of the file
  // was reached
}
catch (ObjectStreamException e){
   // alert the user that the file is corrupted
}
catch (IOException e){
  // alert the user that some other I/O
  // error occurred
}

JCheckbook 通過(guò)使用多個(gè)catch塊來(lái)給用戶(hù)提供捕獲到異常的明確信息。舉例來(lái)說(shuō):如果捕獲了FileNotFoundException,它可以提示用戶(hù)指定另一 個(gè)文件,某些情況下多個(gè)catch塊帶來(lái)的額外編碼工作量可能是非必要的負(fù)擔(dān),但在這個(gè)例子中,額外的代碼的確幫助程序提供了對(duì)用戶(hù)更友好的響應(yīng)。

除前三個(gè)catch塊處理的異常之外,最后一個(gè)catch塊在IOException拋出時(shí)給用戶(hù)提供了更泛化的錯(cuò)誤信息.這樣一來(lái),程序就可以盡可能提供具體的信息,但也有能力處理未預(yù)料到的其他異常。

有 時(shí)開(kāi)發(fā)人員會(huì)捕獲范化異常,并顯示異常類(lèi)名稱(chēng)或者打印堆棧信息以求"具體"。千萬(wàn)別這么干!用戶(hù)看到java.io.EOFException或者堆棧信息 只會(huì)頭疼而不是獲得幫助。應(yīng)當(dāng)捕獲具體的異常并且用"人話(huà)"給用戶(hù)提示確切的信息。不過(guò),異常堆棧倒是可以在你的日志文件里打印。記住,異常和堆棧信息是用來(lái)幫助開(kāi)發(fā)人 員而不是用戶(hù)的。

最后,應(yīng)該注意到JCheckbook并沒(méi)有在readPreferences()中捕獲異常,而是將捕獲和處理異常留到用戶(hù)界面層來(lái)做,這樣就能用對(duì)話(huà)框或其他方式來(lái)通知用戶(hù)。這被稱(chēng)為"延遲捕獲",下文就會(huì)談到。

提早拋出

異常堆棧信息提供了導(dǎo)致異常出現(xiàn)的方法調(diào)用鏈的精確順序,包括每個(gè)方法調(diào)用的類(lèi)名,方法名,代碼文件名甚至行數(shù),以此來(lái)精確定位異常出現(xiàn)的現(xiàn)場(chǎng)。

java.lang.NullPointerException
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:103)
at jcheckbook.JCheckbook.readPreferences(JCheckbook.java:225)
at jcheckbook.JCheckbook.startup(JCheckbook.java:116)
at jcheckbook.JCheckbook.<init>(JCheckbook.java:27)
at jcheckbook.JCheckbook.main(JCheckbook.java:318)

以 上展示了FileInputStream類(lèi)的open()方法拋出NullPointerException的情況。不過(guò)注意 FileInputStream.close()是標(biāo)準(zhǔn)Java類(lèi)庫(kù)的一部分,很可能導(dǎo)致這個(gè)異常的問(wèn)題原因在于我們的代碼本身而不是Java API。所以問(wèn)題很可能出現(xiàn)在前面的其中一個(gè)方法,幸好它也在堆棧信息中打印出來(lái)了。

不幸的是,NullPointerException是Java中信息量最少的(卻也是最常遭遇且讓人崩潰的)異常。它壓根不提我們最關(guān)心的事情:到底哪里是null。所以我們不得不回退幾步去找哪里出了錯(cuò)。

通過(guò)逐步回退跟蹤堆棧信息并檢查代碼,我們可以確定錯(cuò)誤原因是向readPreferences()傳入了一個(gè)空文件名參數(shù)。既然readPreferences()知道它不能處理空文件名,所以馬上檢查該條件:

public void readPreferences(String filename)
throws IllegalArgumentException{
  if (filename == null){
     throw new IllegalArgumentException("filename is null");
  } //if
 
  //...perform other operations...
 
  InputStream in = new FileInputStream(filename);
 
  //...read the preferences file...
}

通過(guò)提早拋出異常(又稱(chēng)"迅速失敗"),異常得以清晰又準(zhǔn)確。堆棧信息立即反映出什么出了錯(cuò)(提供了非法參數(shù)值),為什么出錯(cuò)(文件名不能為空值),以及哪里出的錯(cuò)(readPreferences()的前部分)。這樣我們的堆棧信息就能如實(shí)提供:

java.lang.IllegalArgumentException: filename is null
at jcheckbook.JCheckbook.readPreferences(JCheckbook.java:207)
at jcheckbook.JCheckbook.startup(JCheckbook.java:116)
at jcheckbook.JCheckbook.<init>(JCheckbook.java:27)
at jcheckbook.JCheckbook.main(JCheckbook.java:318)

另外,其中包含的異常信息("文件名為空")通過(guò)明確回答什么為空這一問(wèn)題使得異常提供的信息更加豐富,而這一答案是我們之前代碼中拋出的NullPointerException所無(wú)法提供的。

通過(guò)在檢測(cè)到錯(cuò)誤時(shí)立刻拋出異常來(lái)實(shí)現(xiàn)迅速失敗,可以有效避免不必要的對(duì)象構(gòu)造或資源占用,比如文件或網(wǎng)絡(luò)連接。同樣,打開(kāi)這些資源所帶來(lái)的清理操作也可以省卻。

延遲捕獲

菜鳥(niǎo)和高手都可能犯的一個(gè)錯(cuò)是,在程序有能力處理異常之前就捕獲它。Java編譯器通過(guò)要求檢查出的異常必須被捕獲或拋出而間接助長(zhǎng)了這種行為。自然而然的做法就是立即將代碼用try塊包裝起來(lái),并使用catch捕獲異常,以免編譯器報(bào)錯(cuò)。

問(wèn) 題在于,捕獲之后該拿異常怎么辦?最不該做的就是什么都不做??盏腸atch塊等于把整個(gè)異常丟進(jìn)黑洞,能夠說(shuō)明何時(shí)何處為何出錯(cuò)的所有信息都會(huì)永遠(yuǎn)丟失。把異常寫(xiě)到日志中還稍微好點(diǎn),至少還有記錄可查。但我們總不能指望用戶(hù)去閱讀或者理解日志文件和異常信息。讓readPreferences()顯示錯(cuò)誤信息對(duì)話(huà)框也不合適,因?yàn)殡m然JCheckbook目前是桌面應(yīng)用程序,但我們還計(jì)劃將它變成基于HTML的Web應(yīng)用。那樣的話(huà),顯示錯(cuò)誤對(duì)話(huà)框顯然不是個(gè)選擇。同時(shí),不管HTML還是C/S版本,配置信息都是在服務(wù)器上讀取的,而錯(cuò)誤信息需要顯示給Web瀏覽器或者客戶(hù)端程序。 readPreferences()應(yīng)當(dāng)在設(shè)計(jì)時(shí)將這些未來(lái)需求也考慮在內(nèi)。適當(dāng)分離用戶(hù)界面代碼和程序邏輯可以提高我們代碼的可重用性。

在有條件處理異常之前過(guò)早捕獲它,通常會(huì)導(dǎo)致更嚴(yán)重的錯(cuò)誤和其他異常。例如,如果上文的readPreferences()方法在調(diào)用FileInputStream構(gòu)造方法時(shí)立即捕獲和記錄可能拋出的FileNotFoundException,代碼會(huì)變成下面這樣:

public void readPreferences(String filename){
  //...
 
  InputStream in = null;
 
  // DO NOT DO THIS!!!
try{
  in = new FileInputStream(filename);
}
catch (FileNotFoundException e){
  logger.log(e);
}
 
in.read(...);
 
//...
}

上面的代碼在完全沒(méi)有能力從FileNotFoundException中恢復(fù)過(guò)來(lái)的情況下就捕獲了它。如果文件無(wú)法找到,下面的方法顯然無(wú)法讀取它。如果 readPreferences()被要求讀取不存在的文件時(shí)會(huì)發(fā)生什么情況?當(dāng)然,F(xiàn)ileNotFoundException會(huì)被記錄下來(lái),如果我們 當(dāng)時(shí)去看日志文件的話(huà),就會(huì)知道。然而當(dāng)程序嘗試從文件中讀取數(shù)據(jù)時(shí)會(huì)發(fā)生什么?既然文件不存在,變量in就是空的,一個(gè) NullPointerException就會(huì)被拋出。

調(diào)試程序時(shí),本能告訴我們要看日志最后面的信息。那將會(huì)是NullPointerException,非常讓人討厭的是這個(gè)異常非常不具體。錯(cuò)誤信息不僅誤導(dǎo)我們什么出了錯(cuò)(真正的錯(cuò)誤是FileNotFoundException而不是NullPointerException),還誤導(dǎo)了錯(cuò)誤的出處。真正 的問(wèn)題出在拋出NullPointerException處的數(shù)行之外,這之間有可能存在好幾次方法的調(diào)用和類(lèi)的銷(xiāo)毀。我們的注意力被這條小魚(yú)從真正的錯(cuò)誤處吸引了過(guò)來(lái),一直到我們往回看日志才能發(fā)現(xiàn)問(wèn)題的源頭。

既然readPreferences() 真正應(yīng)該做的事情不是捕獲這些異常,那應(yīng)該是什么?看起來(lái)有點(diǎn)有悖常理,通常最合適的做法其實(shí)是什么都不做,不要馬上捕獲異常。把責(zé)任交給 readPreferences()的調(diào)用者,讓它來(lái)研究處理配置文件缺失的恰當(dāng)方法,它有可能會(huì)提示用戶(hù)指定其他文件,或者使用默認(rèn)值,實(shí)在不行的話(huà)也 許警告用戶(hù)并退出程序。

把異常處理的責(zé)任往調(diào)用鏈的上游傳遞的辦法,就是在方法的throws子句聲明異常。在聲明可能拋出的異常時(shí),注意越具體越好。這用于標(biāo)識(shí)出調(diào)用你方法的程序需要知曉并且準(zhǔn)備處理的異常類(lèi)型。例如,“延遲捕獲”版本的readPreferences()可能是這樣的:

public void readPreferences(String filename)
throws IllegalArgumentException,
FileNotFoundException, IOException{
  if (filename == null){
      throw new IllegalArgumentException("filename is null");
   } //if
 
   //...
 
   InputStream in = new FileInputStream(filename);
 
//...
}

技 術(shù)上來(lái)說(shuō),我們唯一需要聲明的異常是IOException,但我們明確聲明了方法可能拋出FileNotFoundException。 IllegalArgumentException不是必須聲明的,因?yàn)樗欠菣z查性異常(即RuntimeException的子類(lèi))。然而聲明它是為 了文檔化我們的代碼(這些異常也應(yīng)該在方法的JavaDocs中標(biāo)注出來(lái))。

當(dāng) 然,最終你的程序需要捕獲異常,否則會(huì)意外終止。但這里的技巧是在合適的層面捕獲異常,以便你的程序要么可以從異常中有意義地恢復(fù)并繼續(xù)下去,而不導(dǎo)致更 深入的錯(cuò)誤;要么能夠?yàn)橛脩?hù)提供明確的信息,包括引導(dǎo)他們從錯(cuò)誤中恢復(fù)過(guò)來(lái)。如果你的方法無(wú)法勝任,那么就不要處理異常,把它留到后面捕獲和在恰當(dāng)?shù)膶用嫣幚怼?/p>

總結(jié)

經(jīng)驗(yàn)豐富的開(kāi)發(fā)人員都知道,調(diào)試程序的最大難點(diǎn)不在于修復(fù)缺陷,而在于從海量的代碼中找出缺陷的藏身之處。只要遵循本文的三個(gè)原則,就能讓你的異常協(xié)助你跟蹤和消滅缺陷,使你的程序更加健壯,對(duì)用戶(hù)更加友好。以上就是這篇文章的全部?jī)?nèi)容,希望能對(duì)大家的學(xué)習(xí)或者工作帶來(lái)一定的幫助。

相關(guān)文章

  • java8的stream如何取max

    java8的stream如何取max

    這篇文章主要介紹了java8的stream如何取max問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Java實(shí)例講解枚舉enum的實(shí)現(xiàn)

    Java實(shí)例講解枚舉enum的實(shí)現(xiàn)

    枚舉法的本質(zhì)就是從所有候選答案中去搜索正確的解,枚舉算法簡(jiǎn)單粗暴,他暴力的枚舉所有可能,盡可能地嘗試所有的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • java實(shí)現(xiàn)自動(dòng)回復(fù)聊天機(jī)器人

    java實(shí)現(xiàn)自動(dòng)回復(fù)聊天機(jī)器人

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)自動(dòng)回復(fù)聊天機(jī)器人,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • Java面試題沖刺第十九天--數(shù)據(jù)庫(kù)(4)

    Java面試題沖刺第十九天--數(shù)據(jù)庫(kù)(4)

    這篇文章主要為大家分享了最有價(jià)值的三道關(guān)于數(shù)據(jù)庫(kù)的面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 五種JAVA GUI布局管理的方式

    五種JAVA GUI布局管理的方式

    這篇文章主要介紹了JAVA幾種GUI布局管理的相關(guān)知識(shí),文中代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • Spring Boot中操作使用Redis實(shí)現(xiàn)詳解

    Spring Boot中操作使用Redis實(shí)現(xiàn)詳解

    Spring Boot與Redis結(jié)合使用,通過(guò)使用Spring Data Redis來(lái)實(shí)現(xiàn)對(duì)Redis的操作,實(shí)現(xiàn)數(shù)據(jù)緩存和高效存儲(chǔ),提高應(yīng)用程序的性能和響應(yīng)速度??梢岳肧pring Boot自帶的Redis Starter方便地集成和配置Redis
    2023-04-04
  • gateway基本配置教程

    gateway基本配置教程

    路由(Route)由一個(gè)ID,一個(gè)目標(biāo)URI(最終路由到的url地址),一組斷言(匹配條件判斷)和一組過(guò)濾器定義,這篇文章主要介紹了gateway基本配置,需要的朋友可以參考下
    2023-05-05
  • 什么是Java自旋鎖

    什么是Java自旋鎖

    這篇文章主要介紹了什么是Java自旋鎖,在有些場(chǎng)景中,同步資源的鎖定時(shí)間很短,為了這一小段時(shí)間去切換線(xiàn)程,線(xiàn)程掛起和恢復(fù)現(xiàn)場(chǎng)的花費(fèi)可能會(huì)讓系統(tǒng)得不償失,下面來(lái)了解具體內(nèi)容介紹吧
    2022-01-01
  • MybatisPlus使用代碼生成器遇到的小問(wèn)題(推薦)

    MybatisPlus使用代碼生成器遇到的小問(wèn)題(推薦)

    這篇文章主要介紹了MybatisPlus使用代碼生成器遇到的小問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 深入探究 spring-boot-starter-parent的作用

    深入探究 spring-boot-starter-parent的作用

    這篇文章主要介紹了spring-boot-starter-parent的作用詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,感興趣的小伙伴可以跟著小編一起來(lái)學(xué)習(xí)一下
    2023-05-05

最新評(píng)論