<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Java中的Checked Exception——美麗世界中潛藏的惡魔?

        在使用Java編寫應(yīng)用的時(shí)候,我們常常需要通過第三方類庫來幫助我們完成所需要的功能。有時(shí)候這些類庫所提供的很多API都通過throws聲明了它們所可能拋出的異常。但是在查看這些API的文檔時(shí),我們卻沒有辦法找到有關(guān)這些異常的詳盡解釋。在這種情況下,我們不能簡(jiǎn)單地忽略這些由throws所聲明的異常:

      1 public void shouldNotThrowCheckedException() {
      2     // 該API調(diào)用可能拋出一個(gè)不明原因的Checked Exception
      3     exceptionalAPI();
      4 }

        否則Java編譯器會(huì)由于shouldNotThrowCheckedException()函數(shù)沒有聲明其可能拋出的Checked Exception而報(bào)錯(cuò)。但是如果通過throws標(biāo)明了該函數(shù)所可能拋出的Checked Exception,那么其它對(duì)shouldNotThrowCheckedException()函數(shù)的調(diào)用同樣需要通過throws標(biāo)明其可能拋出該Checked Exception。

        哦,這可真是一件令人煩燥的事情。那我們應(yīng)該如何對(duì)這些Checked Exception進(jìn)行處理呢?在本文中,我們將對(duì)如何在Java應(yīng)用中使用及處理Checked Exception進(jìn)行簡(jiǎn)單地介紹。

       

      Java異常簡(jiǎn)介

        在詳細(xì)介紹Checked Exception所導(dǎo)致的問題之前,我們先用一小段篇幅簡(jiǎn)單介紹一下Java中的異常。

        在Java中,異常主要分為三種:Exception,RuntimeException以及Error。這三類異常都是Throwable的子類。直接從Exception派生的各個(gè)異常類型就是我們剛剛提到的Checked Exception。它的一個(gè)比較特殊的地方就是強(qiáng)制調(diào)用方對(duì)該異常進(jìn)行處理。就以我們常見的用于讀取一個(gè)文件內(nèi)容的FileReader類為例。在該類的構(gòu)造函數(shù)聲明中聲明了其可能會(huì)拋出FileNotFoundException:

      1 public FileReader(String fileName) throws FileNotFoundException {
      2     ……
      3 }

        那么在調(diào)用該構(gòu)造函數(shù)的函數(shù)中,我們需要通過try…catch…來處理該異常:

      1 public void processFile() {
      2     try {
      3         FileReader fileReader = new FileReader(inFile);
      4     } catch(FileNotFoundException exception) {
      5         // 異常處理邏輯
      6     }
      7     ……
      8 }

        如果我們不通過try…catch…來處理該異常,那么我們就不得不在函數(shù)聲明中通過throws標(biāo)明該函數(shù)會(huì)拋出FileNotFoundException:

      1 public void processFile() throws FileNotFoundException {
      2     FileReader fileReader = new FileReader(inFile); // 可能拋出FileNotFoundException
      3     ……
      4 }

        而RuntimeException類的各個(gè)派生類則沒有這種強(qiáng)制調(diào)用方對(duì)異常進(jìn)行處理的需求。為什么這兩種異常會(huì)有如此大的區(qū)別呢?因?yàn)镽untimeException所表示的是軟件開發(fā)人員沒有正確地編寫代碼所導(dǎo)致的問題,如數(shù)組訪問越界等。而派生自Exception類的各個(gè)異常所表示的并不是代碼本身的不足所導(dǎo)致的非正常狀態(tài),而是一系列應(yīng)用本身也無法控制的情況。例如一個(gè)應(yīng)用在嘗試打開一個(gè)文件并寫入的時(shí)候,該文件已經(jīng)被另外一個(gè)應(yīng)用打開從而無法寫入。對(duì)于這些情況,Java通過Checked Exception來強(qiáng)制軟件開發(fā)人員在編寫代碼的時(shí)候就考慮對(duì)這些無法避免的情況的處理,從而提高代碼質(zhì)量。

        而Error則是一系列很難通過程序解決的問題。這些問題基本上是無法恢復(fù)的,例如內(nèi)存空間不足等。在這種情況下,我們基本無法使得程序重新回到正常軌道上。因此一般情況下,我們不會(huì)對(duì)從Error類派生的各個(gè)異常進(jìn)行處理。而且由于其實(shí)際上與本文無關(guān),因此我們不再對(duì)其進(jìn)行詳細(xì)講解。

       

      天使變惡魔

        既然Java中的Checked Exception能夠提高用戶代碼質(zhì)量,為什么還有那么多人反對(duì)它呢?原因很簡(jiǎn)單:它太容易被誤用了。而在本節(jié)中,我們就將列出這些誤用情況并提出相應(yīng)的網(wǎng)絡(luò)上最為推薦的解決方案。

       

      無處不在的throws

        第一種誤用的情況就是Checked Exception的廣泛傳播。在前面已經(jīng)提到過,調(diào)用一個(gè)可能拋出Checked Exception的API時(shí),軟件開發(fā)人員可以有兩種選擇。其中一種選擇就是在對(duì)該API進(jìn)行調(diào)用的函數(shù)上添加throws聲明,并將該Checked Exception向上傳遞:

      1 public void processFile() throws FileNotFoundException {
      2     FileReader fileReader = new FileReader(inFile); // 可能拋出FileNotFoundException
      3     ……
      4 }

        而在調(diào)用processFile()函數(shù)的代碼中,軟件開發(fā)人員可能覺得這里還不是處理異常FileNotFoundException的合適地點(diǎn),因此他通過throws將該異常再次向上傳遞。但是在一個(gè)函數(shù)上添加throws意味著其它對(duì)該函數(shù)進(jìn)行調(diào)用的代碼同樣需要處理該throws聲明。在一個(gè)代碼復(fù)用性比較好的系統(tǒng)中,這些throws會(huì)非常快速地蔓延開來:

        從上圖中已經(jīng)可以看出:如果不去處理Checked Exception,而是將其通過throws拋出,那么會(huì)有越來越多的函數(shù)受到影響。在這種情況下,我們要在多處對(duì)該Checked Exception進(jìn)行處理。

        如果在蔓延的過程中所遇到的是一個(gè)函數(shù)的重載或者接口的實(shí)現(xiàn),那么事情就會(huì)變得更加麻煩了。這是因?yàn)橐粋€(gè)函數(shù)聲明中的throws實(shí)際上是函數(shù)簽名的一部分。如果在函數(shù)重載或接口實(shí)現(xiàn)中添加了一個(gè)throws,那么為了保持原有的關(guān)系,被重載的函數(shù)或被實(shí)現(xiàn)的接口中的相應(yīng)函數(shù)同樣需要添加一個(gè)throws聲明。而這樣的改動(dòng)則會(huì)導(dǎo)致其它函數(shù)重載及接口實(shí)現(xiàn)同樣需要更改:

        在上圖中,我們顯示了在一個(gè)接口聲明中添加throws的嚴(yán)重后果。在一開始,我們?cè)趹?yīng)用中實(shí)現(xiàn)了接口函數(shù)Interface::method()。此時(shí)在應(yīng)用以及第三方應(yīng)用中擁有六種對(duì)它的實(shí)現(xiàn)。但是如果A::method()的實(shí)現(xiàn)中拋出了一個(gè)Checked Exception,那么其就會(huì)要求接口中的相應(yīng)函數(shù)也添加該throws聲明。一旦在接口中添加了throws聲明,那么在應(yīng)用以及第三方應(yīng)用中的所有對(duì)該接口的實(shí)現(xiàn)都需要添加該throws聲明,即使在這些實(shí)現(xiàn)中并不存在可能拋出該異常的函數(shù)調(diào)用。

        那么我們應(yīng)該怎么解決這個(gè)問題呢?首先,我們應(yīng)該盡早地對(duì)Checked Exception進(jìn)行處理。這是因?yàn)殡S著Checked Exception沿著函數(shù)調(diào)用的軌跡向上傳遞的過程中,這些被拋出的Checked Exception的意義將逐漸模糊。例如在startupApplication()函數(shù)中,我們可能需要讀取用戶的配置文件來根據(jù)用戶的原有偏好配置應(yīng)用。由于該段邏輯需要讀取用戶的配置文件,因此其內(nèi)部邏輯在運(yùn)行時(shí)將可能拋出FileNotFoundException。如果這個(gè)FileNotFoundException沒有及時(shí)地被處理,那么startupApplication()函數(shù)的簽名將如下所示:

      1 public void startupApplication() throws FileNotFoundException {
      2     ……
      3 }

        在啟動(dòng)一個(gè)應(yīng)用的時(shí)候可能會(huì)產(chǎn)生一個(gè)FileNotFoundException異常?是的,這很容易理解,但是到底哪里發(fā)生了異常?讀取偏好文件的時(shí)候還是加載Dll的時(shí)候?應(yīng)用或用戶需要針對(duì)該異常進(jìn)行什么樣的處理?此時(shí)我們所能做的只能是通過分析該異常實(shí)例中所記錄的信息來判斷到底哪里有異常。

        反過來,如果我們?cè)诋a(chǎn)生Checked  Exception的時(shí)候立即對(duì)該異常進(jìn)行處理,那么此時(shí)我們將擁有有關(guān)該異常的最為豐富的信息:

      1 public void readPreference() {
      2     ……
      3     try {
      4         FileReader fileReader = new FileReader(preferenceFile);
      5     } catch(FileNotFoundException exception) {
      6         // 在日志中添加一條記錄并使用默認(rèn)設(shè)置
      7     }
      8     ……
      9 }

        但是在用戶那里看來,他曾經(jīng)所設(shè)置的偏好在這次使用時(shí)候已經(jīng)不再有效了。這是我們的程序在運(yùn)行時(shí)所產(chǎn)生的異常情況,因此我們需要通知用戶:因?yàn)樵瓉淼钠梦募辉俅嬖诹耍虼宋覀儗⑹褂媚J(rèn)的應(yīng)用設(shè)置。而這一切則是通過一個(gè)在我們的應(yīng)用中定義的RuntimeException類的派生類來完成的:

       1 public void readPreference() {
       2     ……
       3     try {
       4         FileReader fileReader = new FileReader(preferenceFile);
       5     } catch(FileNotFoundException exception) {
       6         logger.log(“Could not find user preference setting file: {0}” preferenceFile);
       7         throw ApplicationSpecificException(PREFERENCE_NOT_FOUND, exception);
       8     }
       9     ……
      10 }

        可以看到,此時(shí)在catch塊中所拋出的ApplicationSpecificException異常中已經(jīng)包含了足夠多的信息。這樣,我們的應(yīng)用就可以通過捕獲ApplicationSpecificException來統(tǒng)一處理它們并將最為詳盡的信息顯示給用戶,從而通知他因?yàn)闊o法找到偏好文件而使用默認(rèn)設(shè)置:

      1 try {
      2     startApplication();
      3 } catch(ApplicationSpecificException exception) {
      4     showWarningMessage(exception.getMessage());
      5 }

       

      手足無措的API使用者

        另一種和Checked Exception相關(guān)的問題就是對(duì)它的隨意處理。在前面的講解中您或許已經(jīng)知道了,如果一個(gè)Checked Exception不能在對(duì)API進(jìn)行調(diào)用的函數(shù)中被處理,那么該函數(shù)就需要添加throws聲明,從而導(dǎo)致多處代碼需要針對(duì)該Checked Exception進(jìn)行修改。那么好,為了避免這種情況,我們就盡早地對(duì)它進(jìn)行處理。但是在查看該API文檔的時(shí)候,我們卻發(fā)現(xiàn)文檔中并沒有添加任何有關(guān)該Checked Exception的詳細(xì)解釋:

      1 /**
      2  * ……
      3  * throws SomeCheckedException
      4  */
      5 public void someFunction() throws SomeCheckedException {
      6 }

        而且我們也沒有辦法從該函數(shù)的簽名中看出到底為什么這個(gè)函數(shù)會(huì)拋出該異常,進(jìn)而也不知道該異常是否需要對(duì)用戶可見。在這種情況下,我們只有截獲它并在日志中添加一條記錄了事:

      1 try {
      2     someFunction();
      3 } catch(SomeCheckedException exception) {
      4     // 在日志中添加一條記錄
      5 }

                      很顯然,這并不是一種好的做法。而這一切的根本原因則是沒有說清楚到底為什么函數(shù)會(huì)拋出該Checked Exception。因此對(duì)于一個(gè)API編寫者而言,由于throws也是函數(shù)聲明的一部分,因此為一個(gè)函數(shù)所能拋出的Checked Exception添加清晰準(zhǔn)確的文檔實(shí)際上是非常重要的。

       

      疲于應(yīng)付的API用戶

        除了沒有清晰的文檔之外,另一種讓API用戶非常抵觸的就是過度地對(duì)Checked Exception進(jìn)行使用。

        或許您已經(jīng)接觸過類似的情況:一個(gè)類庫中用于取得數(shù)據(jù)的API,如getData(int index),通過throws拋出一個(gè)異常,以表示API用戶所傳入的參數(shù)index是一個(gè)非法值。可以想象得到的是,由于getData()可能會(huì)被非常頻繁地使用,因此軟件開發(fā)人員需要在每一處調(diào)用都使用try … catch …塊來截獲該異常,從而使代碼顯得凌亂不堪。

        如果一個(gè)類庫擁有一個(gè)這樣的API,那么該類庫中的這種對(duì)Checked Exception的不恰當(dāng)使用常常不止一個(gè)。那么該類庫的這些API會(huì)大量地污染用戶代碼,使得這些用戶代碼中充斥著不必要也沒有任何意義的try…catch…塊,進(jìn)而讓代碼邏輯顯得極為晦澀難懂。

       1 Record record = null;
       2 try {
       3     record = library.getDataAt(2);
       4 } catch(InvalidIndexException exception) {
       5     …… // 異常處理邏輯
       6 }
       7 record.setIntValue(record.getIntValue() * 2);
       8 try {
       9     library.setDataAt(2, record);
      10 } catch(InvalidIndexException exception) {
      11     …… // 異常處理邏輯
      12 }

        反過來,如果這些都不是Checked Exception,而且軟件開發(fā)人員也能保證傳入的索引是合法的,那么代碼會(huì)簡(jiǎn)化很多:

      1 Record record = library.getDataAt(2);
      2 record.setIntValue(record.getIntValue() * 2);
      3 library.setDataAt(2, record);

        那么我們應(yīng)該在什么時(shí)候使用Checked Exception呢?就像前面所說的,如果一個(gè)異常所表示的并不是代碼本身的不足所導(dǎo)致的非正常狀態(tài),而是一系列應(yīng)用本身也無法控制的情況,那么我們將需要使用Checked Exception。就以前面所列出的FileReader類的構(gòu)造函數(shù)為例:

      1 public FileReader(String fileName) throws FileNotFoundException

        該構(gòu)造函數(shù)的簽名所表示的意義實(shí)際上是:

      1. 必須通過傳入的參數(shù)fileName來標(biāo)示需要打開的文件
      2. 如果文件存在,那么該構(gòu)造函數(shù)將返回一個(gè)FileReader類的實(shí)例
      3. 對(duì)該構(gòu)造函數(shù)進(jìn)行使用的代碼必須處理由fileName所標(biāo)示的文件不存在,進(jìn)而拋出FileNotFoundException的情況

        也就是說,Checked Exception實(shí)際上是API設(shè)計(jì)中的一部分。在調(diào)用這個(gè)API的時(shí)候,你不得不處理目標(biāo)文件不存在的情況。而這則是由文件系統(tǒng)的自身特性所導(dǎo)致的。而之所以Checked Exception導(dǎo)致了如此多的爭(zhēng)論和誤用,更多是因?yàn)槲覀冊(cè)谟卯惓_@個(gè)用來表示應(yīng)用中的運(yùn)行錯(cuò)誤這個(gè)語言組成來通知用戶他所必須處理的應(yīng)用無法控制的可能情況。也就是說,其為異常賦予了新的含義,使得異常需要表示兩個(gè)完全不相干的概念。而在沒有仔細(xì)分辨的情況下,這兩個(gè)概念是極容易混淆的。因此在嘗試著定義一個(gè)Checked Exception之前,API編寫者首先要考慮這個(gè)異常所表示的到底是系統(tǒng)自身缺陷所導(dǎo)致的運(yùn)行錯(cuò)誤,還是要讓用戶自己來處理的邊緣情況。

       

      正確地使用Checked  Exception

        實(shí)際上,如何正確地使用Checked Exception已經(jīng)在前面的各章節(jié)講解中進(jìn)行了詳細(xì)地說明。在這里我們?cè)俅巫鲆粋€(gè)總結(jié),同時(shí)也用來加深一下印象。

        從API編寫者的角度來講,他所需要考慮的就是在何時(shí)使用一個(gè)Checked Exception。

        首先,Checked Exception應(yīng)當(dāng)只在異常情況對(duì)于API以及API的使用者都無法避免的情況下被使用。例如在打開一個(gè)文件的時(shí)候,API以及API的使用者都沒有辦法保證該文件一定存在。反過來,在通過索引訪問數(shù)據(jù)的時(shí)候,如果API的使用者對(duì)參數(shù)index傳入的是-1,那么這就是一個(gè)代碼上的錯(cuò)誤,是完全可以避免的。因此對(duì)于index參數(shù)值不對(duì)的情況,我們應(yīng)該使用Unchecked Exception。

        其次,Checked Exception不應(yīng)該被廣泛調(diào)用的API所拋出。這一方面是基于代碼整潔性的考慮,另一方面則是因?yàn)镃hecked Exception本身的實(shí)際意義是API以及API的使用者都無法避免的情況。如果一個(gè)應(yīng)用有太多處這種“無法避免的異常”,那么這個(gè)程序是否擁有足夠的質(zhì)量也是一個(gè)很值得考慮的問題。而就API提供者而言,在一個(gè)主要的被廣泛使用的功能上拋出這種異常,也是對(duì)其自身API的一種否定。

        再次,一個(gè)Checked Exception應(yīng)該有明確的意義。這種明確意義的標(biāo)準(zhǔn)則是需要讓API使用者能夠看到這個(gè)Checked Exception所對(duì)應(yīng)的異常類,該異常類所包含的各個(gè)域,并閱讀相應(yīng)的API文檔以后就能夠了解到底哪里出現(xiàn)了問題,進(jìn)而向用戶提供準(zhǔn)確的有關(guān)該異常的解釋。

        而對(duì)于API的用戶而言,一旦遇到了一個(gè)API會(huì)拋出Checked Exception,那么他就需要考慮使用一個(gè)Wrapped Exception來將該Checked Exception包裝起來。那什么是Wrapped Exception呢?

        簡(jiǎn)單地說,Wrapped Exception就是將一個(gè)異常包裝起來的異常。在try…catch…塊捕獲到一個(gè)異常的時(shí)候,該異常內(nèi)部所記錄的消息可能并不合適。就以前面我們已經(jīng)舉過的加載偏好的示例為例。在啟動(dòng)時(shí),應(yīng)用會(huì)嘗試讀取用戶的偏好設(shè)置。這些偏好設(shè)置記錄在了一個(gè)文件中,卻可能已經(jīng)被誤刪除。在這種情況下,對(duì)該偏好文件的讀取會(huì)導(dǎo)致一個(gè)FileNotFoundException拋出。但是在該異常中所記錄的信息對(duì)于用戶,甚至應(yīng)用編寫者而言沒有任何價(jià)值:“Could not find file preference.xml while opening file”。在這種情況下,我們就需要構(gòu)造一個(gè)新的異常,在該異常中標(biāo)示準(zhǔn)確的錯(cuò)誤信息,并將FileNotFoundException作為新異常的原因:

       1 public void readPreference() {
       2     ……
       3     try {
       4         FileReader fileReader = new FileReader(preferenceFile);
       5     } catch(FileNotFoundException exception) {
       6         logger.log(“Could not find user preference setting file: {0}” preferenceFile);
       7         throw ApplicationSpecificException(PREFERENCE_NOT_FOUND, exception);
       8     }
       9     ……
      10 }

        上面的示例代碼中重新拋出了一個(gè)ApplicationSpecificException類型的異常。從它的名字就可以看出,其應(yīng)該是API使用者在應(yīng)用實(shí)現(xiàn)中所添加的應(yīng)用特有的異常。為了避免調(diào)用棧中的每一個(gè)函數(shù)都需要添加throws聲明,該異常需要從RuntimeException派生。這樣應(yīng)用就可以通過在調(diào)用棧的最底層捕捉這些異常并對(duì)這些異常進(jìn)行處理:在系統(tǒng)日志中添加一條異常記錄,只對(duì)用戶顯示異常中的消息,以防止異常內(nèi)部的調(diào)用棧信息暴露過多的實(shí)現(xiàn)細(xì)節(jié)等:

      1 try {
      2     ……
      3 } catch(ApplicationSpecificException exception) {
      4     logger.log(exception.getLevel(), exception.getMessage(), exception);
      5     // 將exception內(nèi)部記錄的信息顯示給用戶(或添加到請(qǐng)求的響應(yīng)中返回)
      6     // 如showWarningMessage(exception.getMessage());
      7 }

       

      轉(zhuǎn)載請(qǐng)注明原文地址并標(biāo)明轉(zhuǎn)載:http://www.rzrgm.cn/loveis715/p/4596551.html

      商業(yè)轉(zhuǎn)載請(qǐng)事先與我聯(lián)系:silverfox715@sina.com

      posted @ 2015-06-23 23:18  loveis715  閱讀(16872)  評(píng)論(4)    收藏  舉報(bào)
      主站蜘蛛池模板: 国产成人午夜福利在线观看| 免费a级毛片18以上观看精品| 亚洲午夜精品久久久久久抢| 2022最新国产在线不卡a| 国产一区二区三区无遮挡| 国产一区二区三区不卡在线看| 国产成人亚洲综合图区| 国产精品熟女乱色一区二区| av午夜久久蜜桃传媒软件| 久久精品国产99国产精品澳门| 好屌草这里只有精品| 无码抽搐高潮喷水流白浆| 伊人久久精品久久亚洲一区| 国产毛片精品一区二区色| 极品少妇无套内射视频| 久热中文字幕在线精品观| 国产精品不卡一区二区三区| 亚洲精品成人久久久| 亚洲啪啪精品一区二区的| 色综合久久中文综合久久激情| 韩国午夜福利片在线观看| 国产精品99久久免费| 爱性久久久久久久久| 国产精品一区二区三区av| 久久亚洲人成网站| 国产盗摄视频一区二区三区| 99热精品毛片全部国产无缓冲| 精品亚洲国产成人av| 亚洲日韩亚洲另类激情文学| 麻豆国产va免费精品高清在线| 少妇久久久被弄到高潮| 亚洲中文字幕一区二区| 欧美成人免费一区二区三区视频 | 97人人添人人澡人人澡人人澡| 国产精品久久一区二区三区| 麻豆人妻| 亚洲精品男男一区二区| 日韩高清免费一码二码三码| 伊人久久大香线蕉av五月天| 欧美一进一出抽搐大尺度视频 | 国产精品国产高清国产av|