版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、<p><b> 目 錄</b></p><p><b> 一 引言1</b></p><p> 二 關(guān)于JDK和Java異常2</p><p> ?。ㄒ唬㎎ava異常定義2</p><p> (二)Java中的異常類2</p><p>
2、 (三)Java語言規(guī)范對異常處理的要求4</p><p> 三 Java異常的處理機制4</p><p> ?。ㄒ唬㎎ava異常處理機制具體實現(xiàn)4</p><p> (二)Java中異常發(fā)生的原因6</p><p> ?。ㄈ㎎ava異常處理的優(yōu)點6</p><p> 四 Java異常處理技術(shù)及其
3、應(yīng)用8</p><p><b> ?。ㄒ唬伋霎惓?</b></p><p><b> ?。ǘ┎东@異常8</b></p><p><b> ?。ㄈ┒褩?</b></p><p> ?。ㄋ模┳远x異常11</p><p> ?。ㄎ澹╆P(guān)于圖形
4、界面程序的異常捕獲12</p><p> 五 Java異常處理基本原則14</p><p> (一)不要忽略已檢查型異常14</p><p> (二)不要一次捕獲所有的異常15</p><p> (三)使用finally塊釋放資源16</p><p> (四)異常不能影響對象的狀態(tài)17</
5、p><p> (五)注意丟失(或忽略)的異常17</p><p> (六)不要同時使用異常機制和返回值來處理異常20</p><p> (七)不要讓try塊過于龐大21</p><p><b> 六 結(jié)束語21</b></p><p><b> 致謝21</b&g
6、t;</p><p><b> 參考文獻22</b></p><p><b> 附錄、源代碼22</b></p><p> 基于Java的異常處理技術(shù)及其應(yīng)用</p><p> 摘 要 本文以Java軟件開發(fā)工具箱JDK1.5.0作為環(huán)境,通過與一些編程語言的錯誤處理相比較,介紹了J
7、ava中異常處理技術(shù)的由來、定義、規(guī)范和Java內(nèi)建的異常類,討論了Java中異常產(chǎn)生的原因、實現(xiàn),還對Java異常處理機制的優(yōu)點進行了簡述,就Java異常處理技術(shù)從拋出異常、捕獲異常、堆棧幀、自定義異常、圖形界面程序異常捕獲五個方面進行了討論。本文還就Java異常處理技術(shù)的應(yīng)用做了一些探討,試圖從軟件開發(fā)者的角度說明Java異常處理技術(shù)在開發(fā)中一些有價值的應(yīng)用。</p><p> 關(guān)鍵詞 Java語言 J
8、ava異常處理 JVM </p><p><b> 一 引言</b></p><p> 在程序設(shè)計中,盡管編寫檢查和處理錯誤的程序代碼很乏味,并且它們使程序源代碼顯得冗長,但是,錯誤檢測和處理仍是任何健壯應(yīng)用程序最重要的組成部分之一。傳統(tǒng)的異常處理多采用返回值來標識程序中出現(xiàn)的異常情況,這是程序員熟悉的一種方式,但卻有很多缺點。首先,一個API(Appli
9、cation Programming Interface 應(yīng)用編程接口)可以返回任意的返回值,而這些返回值本身并不能解釋是否代表一個異常情況發(fā)生或者發(fā)生異常的具體情況,需要調(diào)用API的程序自己判斷并解釋返回值的含義。其次,它并沒有一種機制來保證異常情況一定會得到處理,調(diào)用程序可以簡單地忽略該返回值,從而需要調(diào)用API的程序員記住去檢測返回值并處理異常情況。這種方式還讓程序代碼變得晦澀冗長, 當(dāng)進行容易出現(xiàn)異常情況的處理時,比如:I/O操
10、作,程序中會出現(xiàn)很大的部分用于處理異常情況的switch分支,程序代碼的可讀性變得很差。</p><p> 相對于傳統(tǒng)異常處理方式的缺點,Java異常處理機制提供了很好的解決方案。Java異常處理使開發(fā)人員不用編寫特殊代碼檢測返回值就能夠很容易地檢測錯誤,并且把異常處理代碼和異常產(chǎn)生代碼明確地分開,通過拋出Java預(yù)定義或者自定義的異常,能夠表明程序中出現(xiàn)了什么樣的異常情況[1];而且Java的語言機制保證了異
11、常一定會得到恰當(dāng)?shù)奶幚?,合理地使用異常處理機制,會讓程序代碼更清晰。 </p><p> 二 關(guān)于JDK和Java異常</p><p> ?。ㄒ唬㎎ava異常定義 </p><p> 早期的編程語言(比如C語言)沒有異常處理,通常是遇到錯誤返回一個特殊的值或設(shè)定一個標志,并以此判斷是不是有錯誤產(chǎn)生。隨著系統(tǒng)規(guī)模的不斷擴大,這種錯誤處理已經(jīng)成為創(chuàng)建大型可維護程序
12、的障礙了。于是在一些語言中出現(xiàn)了異常處理機制,比如在Basic中的異常處理語句“on error goto”,而Java則是在C++基礎(chǔ)上建立了新的異常處理機制。</p><p> Java通過面向?qū)ο蟮姆椒ㄟM行異常處理,把各種不同的異常進行分類,并提供了良好的接口。這種機制為復(fù)雜程序提供了強有力的控制方式。同時這些異常代碼與“常規(guī)”代碼分離,增強了程序的可讀性,編寫程序時也顯得更靈活。 在編譯時沒有問
13、題的Java源程序,在運行期可能還會發(fā)生錯誤,這種錯誤就稱為異常(Exception)。異常也可認為是程序運行過程中違背正常指令流而產(chǎn)生的事件[2]。</p><p> (二)Java中的異常類</p><p> 在Java中,所有的異常都是以類對象的形式存在的。每個異常都是Throwable類或其子類的實例。當(dāng)一個方法出現(xiàn)異常后便拋出一個異常對象,該對象中包含有異常信息,調(diào)用這個對象
14、的方法可以捕獲到這個異常并進行處理[3]。</p><p> Throwable是所有異常的基類,程序中一般不會直接拋出Throwable對象,Exception和Error是Throwable的子類,Exception下面又有RuntimeException和一般的Exception兩類??梢园袹ava異常分為三類:</p><p> 第一類是Error,Error表示程序在運行期間
15、出現(xiàn)了十分嚴重、不可恢復(fù)的錯誤,在這種情況下應(yīng)用程序只能終止運行,例如Java虛擬機(JVM)出現(xiàn)錯誤。在EJB(Enterprise JavaBean)中將此類歸為JVM 異常,這種類型的異常由 JVM 拋出。Error是一種uncheckedException(未檢查型異常),編譯器不會檢查Error是否被處理,在程序中不用捕獲Error類型的異常;一般情況下,在程序中也不應(yīng)該拋出Error類型的異常。</p><
16、;p> 第二類是RuntimeException,在EJB也稱為系統(tǒng)異常。RuntimeException是一種未檢查型異常,即表示編譯器不會檢查程序是否對RuntimeException作了處理,在程序中不必捕獲未檢查型異常,也不必在方法體聲明拋出RuntimeException類。RuntimeException發(fā)生的時候,表示程序中出現(xiàn)了編程錯誤,所以應(yīng)該找出錯誤修改程序,而不是去捕獲RuntimeException。&l
17、t;/p><p><b> ……</b></p><p> …… …… </p><p> 圖2-1 Java異常類層次圖</p><p> 第三類是一般的checkedException, 即已檢查型異常。在EJB中稱為應(yīng)用程序異常。已檢查型異常是在編程中使用最多的異常,所
18、有繼承自Exception并且不是RuntimeException的異常都是已檢查型異常,如圖2-1中為Java異常的類層次圖。</p><p> Java系統(tǒng)包java.lang、java.util、java.io和java.net中都聲明有標準異常類。這些異常類以未檢查型異常類和已檢查型異常類可分為如下:</p><p> 已檢查型異常類主要有:</p><p&
19、gt; (1)在java.lang中:</p><p> ClassNotFoundException:指定名字的類或接口沒有被發(fā)現(xiàn)。</p><p> CloneNotSupportedException:克隆一個沒有實現(xiàn)Cloneable接口的類。</p><p> IllegalAccessException:試圖使用給出了完全路徑信息的字符串加載一個
20、類,但當(dāng)前正在執(zhí)行的方法無法訪問指定的類,原因是該類不是public類或是在另一個包中。</p><p> InstantiationException:試圖使用Class的newInstance方法創(chuàng)建一個對象實例,但是,指定的對象沒有被實例化,因為它是一個接口、抽象類或者一個數(shù)組。</p><p> InterruptedException:當(dāng)前的線程正在等待,而另一個線程則使用了
21、Thread的interrupt方法中斷了當(dāng)前線程。</p><p> ?。?)在Java.io中:</p><p> IOException:申請I/O操作沒有成功。</p><p> EOFException:在輸入操作正常結(jié)束前遇到了文件結(jié)束符。</p><p> FileNotFoundException:在文件系統(tǒng)中,沒有找到
22、由文件名字符串指定的文件。</p><p> InterruptedIOException當(dāng)前線程正在等待I/O操作的完成,而另一個線程使用thread的interrupt方法中斷了當(dāng)前線程。</p><p> 未檢查型異常類主要有:</p><p> (1)在java.lang中:</p><p> ArithmeticExcept
23、ion:表示遇到了算術(shù)的異常問題,例如0作為除數(shù)。</p><p> ArrayStoreException:試圖把與數(shù)組類型不相符的值存入數(shù)組。</p><p> ClassCastException:試圖把一個對象的引用強制轉(zhuǎn)換為不合適的類型。</p><p> InderOutOfBoundsException:數(shù)組的下標越界。</p>&
24、lt;p> NullPointerException:試圖使用一個空的對象引用。</p><p> SecurityException:檢測到了違反安全的行為。</p><p> ?。?)在java.util中:</p><p> EmptyStaceException:試圖訪問一個空堆棧中的一個元素。</p><p> NoS
25、uchElementException:試圖訪問一個空向量中的元素。</p><p> ?。ㄈ㎎ava語言規(guī)范對異常處理的要求</p><p> Java語言規(guī)定必須對已檢查型異常作處理,編譯器會對此作檢查,要么在方法體中聲明拋出已檢查型異常,要么使用catch語句捕獲已檢查型異常進行處理,不然不能通過編譯[2]。已檢查型異常用于以下環(huán)境:</p><p>
26、(1)該異常發(fā)生后是可以被恢復(fù)的,如一個Internet連接發(fā)生異常被終止后,可以重新連接再進行后續(xù)操作。</p><p> ?。?)程序依賴于不可靠的外部條件,該依賴條件可能出錯,如系統(tǒng)I/O。</p><p> (3)該異常發(fā)生后并不會導(dǎo)致程序處理錯誤,進行一些處理后可以繼續(xù)后續(xù)操作。</p><p> Java語言規(guī)范中將任何Error的子類以及Runti
27、meException的子類都稱為未檢查型異常。而其它異常都稱為已檢查型異常。</p><p> 三 Java異常的處理機制</p><p> ?。ㄒ唬㎎ava異常處理機制具體實現(xiàn)</p><p> 在Java程序運行出現(xiàn)異常時,發(fā)生異常的程序段要拋出異常,而運行系統(tǒng)負責(zé)尋找一段代碼來處理異常。Java語言中,創(chuàng)建一個異常對象并將它交給運行系統(tǒng)稱為拋出一個異常
28、,又稱為異常拋出。</p><p> 在Java中,一個方法要拋出異常,必須遵循一定的規(guī)定,即所謂的“異常規(guī)范”。異常規(guī)范采用了一個額外的關(guān)鍵字:throws。要使方法拋出異常,應(yīng)在方法聲明中,位于參變量列表的后面(即throws后面)列舉全部潛在的異常類型。</p><p> 假若Java中的某方法可能拋出一個異常,但程序中沒有對其進行控制,編譯器會偵測到這個情況,通知程序員必須在此
29、方法內(nèi)進行異??刂?或者從方法里拋出一個異常。通過遵守異常規(guī)范,Java可在編譯期保證異常處理的正確性。</p><p> 當(dāng)Java拋出一個異常,程序?qū)膶?dǎo)致異常的代碼處跳出,JVM檢測尋找和try關(guān)鍵字匹配的處理該異常的catch塊,如果找到,將控制權(quán)交到catch塊中的代碼,然后繼續(xù)往下執(zhí)行程序, 如果沒有找到處理該異常的catch塊,try塊中發(fā)生異常的代碼不會被重新執(zhí)行,在所有的finally塊代碼被
30、執(zhí)行和當(dāng)前線程的所屬的ThreadGroup的uncaughtException方法被調(diào)用后,遇到異常的當(dāng)前線程被終止。</p><p> Java異常處理的語句形式如下:</p><p><b> try</b></p><p><b> {</b></p><p> //正常執(zhí)行的代碼可
31、能產(chǎn)生異常</p><p> throw(異常類1 e,異常類2 e,……異常類n e)</p><p><b> }</b></p><p> catch(異常類1 e)</p><p><b> {</b></p><p> //異常類1的處理代碼</p&
32、gt;<p><b> }</b></p><p> catch(異常類2 e)</p><p><b> {</b></p><p> //異常類2的處理代碼</p><p><b> }</b></p><p><b&g
33、t; ……</b></p><p> catch(異常類n e)</p><p><b> {</b></p><p> //異常類n的處理代碼</p><p><b> finally</b></p><p><b> {</b>
34、;</p><p> //執(zhí)行清除工作的語句</p><p><b> }</b></p><p> Java程序運行在try塊中,如果產(chǎn)生了異常,則不再運行try塊下面的語句,而直接進入catch塊中,尋找第一個與之匹配的異常類型。try/catch語句會自動在try塊后面的各個catch塊中,找出與該異常類相匹配的參數(shù)。如果該參數(shù)符合
35、以下三個條件之一時,則認為這個參數(shù)與產(chǎn)生的異常相匹配[4]。</p><p> (1)參數(shù)與產(chǎn)生的異常屬于同一個類。</p><p> (2)參數(shù)是產(chǎn)生異常的父類。</p><p> (3)參數(shù)是一個接口,產(chǎn)生的異常實現(xiàn)了這一個接口。</p><p> 當(dāng)產(chǎn)生的異常找到了第一個與之相匹配的參數(shù)時,就執(zhí)行這一參數(shù)的catch塊中的Jav
36、a代碼。執(zhí)行完catch塊后,程序恢復(fù)執(zhí)行,但不會回到異常發(fā)生處繼續(xù)執(zhí)行而是執(zhí)行try/catch結(jié)構(gòu)后面的代碼。</p><p> finally語句可以說是為異常處理事件提供的一個清理機構(gòu)。一般是用來關(guān)閉文件或者釋放其他的系統(tǒng)資源作為try/catch/finally結(jié)構(gòu)的一部分,可以沒有finally塊。如果存在finally塊,無論try塊中是否發(fā)生異常,是否執(zhí)行過catch塊,都要執(zhí)行finally塊
37、。用finally塊的一個好處,就是把方法中所有清除狀態(tài)和關(guān)閉系統(tǒng)文件的語句放在一起,不但避免代碼的重復(fù),更是減少出現(xiàn)遺漏語句,對于程序以后的修改也較為集中和方便。</p><p> ?。ǘ㎎ava中異常發(fā)生的原因</p><p> 異常發(fā)生有三種原因:</p><p> ?。?)Java虛擬機檢測到了非正常的執(zhí)行狀態(tài),這些狀態(tài)可能由三種因素之一引起:</
38、p><p> ?、俦磉_式的計算違背了Java語言的語義,例如:數(shù)組越界、除數(shù)為0等。</p><p> ?、谠谳d入和鏈接Java程序時出現(xiàn)錯誤。</p><p> ?、鄢隽讼到y(tǒng)的資源限制,例如使用了太多的內(nèi)存這些無法預(yù)知的異常。</p><p> ?。?)Java程序代碼中的throw語句被執(zhí)行。</p><p> ?。?/p>
39、3)發(fā)生異步異常,其可能的原因有Thread類的stop方法被調(diào)用;JVM內(nèi)部發(fā)生錯誤;運行時庫出現(xiàn)了內(nèi)部錯誤等。</p><p> ?。ㄈ㎎ava異常處理的優(yōu)點</p><p> 在面向過程的傳統(tǒng)語言中對程序中可能出現(xiàn)的錯誤一般采取:查錯、報錯和排錯的處理錯誤代碼和常規(guī)代碼混雜在一起的方法。比如在C語言(面向過程的一種編程語言)中輸出流的處理偽代碼是:</p><
40、p> {建立一個文件輸入流;</p><p> if(建立不成功){ 報錯;處理錯誤;}</p><p> else { 從輸入流中讀入一個字符;</p><p> if(讀入不成功){ 報錯;處理錯誤;關(guān)閉該文件的輸入流</p><p> if(關(guān)閉不成功) { 報錯;處理錯誤; }</p><p>
41、 else { …… }</p><p><b> }</b></p><p><b> else</b></p><p><b> …… </b></p><p><b> }</b></p><p> 經(jīng)過這
42、樣的處理,程序可以保證正常地運行。但是程序的每一步都要考慮是否會發(fā)生錯誤,出現(xiàn)了錯誤又該如何處理的問題。這不但使程序代碼的行數(shù)大大增加,而且,處理錯誤代碼和正常代碼混雜在一起,使程序流程變得十分復(fù)雜,不利于閱讀。此外,它對于同一類型的錯誤也不方便進行統(tǒng)一的處理。</p><p> 于上面等價實現(xiàn)地面向?qū)ο笳Z言處理異常的偽代碼為:</p><p> try{ 建立一個文件輸入流;<
43、/p><p> 讀取一個字符;......;</p><p><b> 關(guān)閉該輸入流;}</b></p><p> catch( 文件輸入流打開失敗 ){ 報錯;處理錯誤;......; }</p><p> catch( 讀入字符失敗 ){ 報錯;處理錯誤;......}</p><p>&
44、lt;b> ......</b></p><p><b> }</b></p><p> 面向?qū)ο笳Z言的異常處理對于面向過程語言處理錯誤的方法有三種好處:其一將處理錯誤代碼和正常代碼分離;其二能夠?qū)惓Q刂{(diào)用堆棧向上傳播;其三能按異常類型和異常對象分組。</p><p> 四 Java異常處理技術(shù)及其應(yīng)用</
45、p><p><b> ?。ㄒ唬伋霎惓?lt;/b></p><p> 如果有一個現(xiàn)成(或者已定義)的異??梢允褂?,則拋出異常很容易。只要滿足以下三點:</p><p> ?。?)找到一個恰當(dāng)?shù)漠惓n?lt;/p><p> ?。?)構(gòu)造一個該類的實例</p><p><b> (3)拋出該實例&l
46、t;/b></p><p> 比如我們經(jīng)常遇到IOException異常發(fā)生,其中它的一個子類是EOFException,就是描述在輸入過程中碰到一個未預(yù)期的文件結(jié)尾標志,我們拋出該異常的方法如下:</p><p> throw new EOFException ( );</p><p><b> 或者這樣:</b></p
47、><p> EOFException e = new EOFException ( );</p><p> throw e ;</p><p><b> ?。ǘ┎东@異常</b></p><p> 要想捕獲一個異常,需要設(shè)置一個try/catch的代碼塊。try塊的最簡單形式如下:</p><
48、p><b> try {</b></p><p><b> code</b></p><p><b> more code</b></p><p><b> }</b></p><p> catch (ExceptionType e ) {
49、</p><p> handler for this type</p><p><b> }</b></p><p> 如果try塊內(nèi)的任何代碼拋出了由catch塊中指定的異常,則程序跳過try塊中的其它代碼,程序執(zhí)行catch塊中的處理代碼。假如try塊沒有任何代碼拋出異常,那么程序會直接跳過catch塊的內(nèi)容。當(dāng)然,可以在try塊同時
50、捕獲多個異常,并分別對每種類型加以不同的處理。</p><p><b> (三)堆棧幀</b></p><p> 在JDK1.4前,通過使用Throwable類的printStacktrace方法來獲得堆棧結(jié)構(gòu)的文本描敘。現(xiàn)在,可以通過調(diào)用getStackTrace方法來獲得一個StackTraceElement對象的數(shù)組,通過研究該數(shù)組,就可以分析程序運行情況。
51、例如:</p><p> Throwable t = new Throwable ( );</p><p> StackTraceElement[ ] frames = t.getStackTrace ( );</p><p> For ( int i= 0;i<frames.length;i++) </p><p> 使用St
52、ackTraceElement類提供的方法獲取文件名以及當(dāng)前執(zhí)行的代碼行行號的方法,同樣,它還提供獲取類名、方法名的方法。而toString方法可以產(chǎn)生一個格式化字符串,其中包含那些獲得的信息。</p><p> 以下程序StackTest.java輸出一個遞歸調(diào)用方法的堆棧情況。</p><p> import java.util.*;</p><p>
53、import javax.swing.*;</p><p> public class StackTest</p><p><b> {</b></p><p> /** 返回 n! = 1 * 2 * . . . * n 的結(jié)果 */</p><p> public static in
54、t factorial (int n)</p><p><b> {</b></p><p> System.out.println ("factorial (" + n + "):" ) ;</p><p> Throwable t = new Throwable( );</p>
55、<p> StackTraceElement[ ] frames = t.getStackTrace();</p><p> for ( int i = 0 ;i < frames.length; i++ )</p><p> System.out.println( frames[ i] );</p><p><b> int
56、r ;</b></p><p> if (n <= 1) r = 1;</p><p> else r = n * factorial(n - 1); // 遞歸調(diào)用factorial()方法</p><p> System.out.println("return " + r);</p><p>
57、;<b> return r;</b></p><p><b> }</b></p><p> public static void main(String[] args)</p><p><b> {</b></p><p> String input = JOpt
58、ionPane.showInputDialog( "請輸入一個整數(shù):" );</p><p> int n =Integer.parseInt(input);</p><p> factorial(n);</p><p> System.exit( 0 ); //退出程序</p><p>
59、;<b> }</b></p><p><b> }</b></p><p> 由于需要從標準輸入中獲取整數(shù)數(shù)字,Integer.ParseInt(input)方法可能會產(chǎn)生java.lang.NumberFormatException異常。運行程序時界面:</p><p> 圖3-1 輸入界面</p>
60、;<p> 如果我們要實現(xiàn)遞歸方法factorial(3),通過getStackTrace方法可以查看程序運行的過程。其結(jié)果為如下:</p><p> 圖3-2 遞歸方法factorial(3)的輸出結(jié)果</p><p> 而這個程序可能產(chǎn)生的一個異常是NumberFormatException異常。當(dāng)異常產(chǎn)生后,JVM調(diào)用該異常處理。其運行界面如下:</p
61、><p> 圖3-3 輸入一個產(chǎn)生異常的數(shù)據(jù)</p><p> 返回到控制臺的結(jié)果為描述該異常:</p><p> 圖3-4 輸入異常數(shù)據(jù)時在控制臺產(chǎn)生的結(jié)果</p><p><b> ?。ㄋ模┳远x異常</b></p><p> 由于在Java中,異常也被看成是對象,而且異常和一
62、般的對象沒有什么不同。因此,任何類都可以定義它自己的異常,并用throw語句引發(fā)它們。其中,throw語句由帶有一個對象的關(guān)鍵字throw組成,這個對象應(yīng)該是Exception或其子類的一個實體對象。要注意的是,當(dāng)執(zhí)行了一條throw語句后,就不會執(zhí)行該語句之后的任何代碼了。</p><p> 通常每個異常類提供一個默認的構(gòu)造器以及一個包含詳細信息的構(gòu)造器。使用Throwable的toString方法會輸出該詳
63、細信息,這個方法對代碼調(diào)試是很有用處。</p><p> 例如創(chuàng)建這樣的異常類:</p><p> class FileFormatException extends IOException {</p><p> public FileFormatException ( ) { </p><p><b> }<
64、;/b></p><p> public FileFormatException ( ) {</p><p> super ( gripe) ;</p><p><b> }</b></p><p><b> }</b></p><p> String re
65、adData ( BufferedReader in ) throws FileFormatException {</p><p><b> ……</b></p><p> while ( ……)</p><p><b> {</b></p><p> if ( ch==-1) /
66、/EOF標記,即文件結(jié)尾</p><p><b> {</b></p><p> if (n<len) </p><p> throw new FileFormatException ( );</p><p> //拋出自定義的異常類FileFormatException</p><
67、p><b> }</b></p><p><b> ……</b></p><p><b> }</b></p><p><b> return s;</b></p><p><b> } </b></p>
68、;<p> ?。ㄎ澹╆P(guān)于圖形界面程序的異常捕獲</p><p> 對于一個非圖形界面的程序而言,如果一個異常沒有被捕獲,則程序會終止運行并且在控制臺輸出一條包含異常類型以及堆棧內(nèi)容的信息。而對于具有圖形界面的程序(包含applet以及應(yīng)用程序),也會輸出這些錯誤信息,但是程序會返回用戶界面外層循環(huán)中去。</p><p> 下面我們從一個有趣的程序-顏色調(diào)和板來討論圖形界面
69、程序處理異常的情況。</p><p> 在SwingColorTest.java(源代碼見附錄)中,程序可以根據(jù)我們輸入紅色、綠色、藍色(RGB)、亮度、飽和度和色度值來調(diào)節(jié)顏色。整個程序除了可能會產(chǎn)生RuntimeException異常外,這個程序還會產(chǎn)生NumberFormatException異常。當(dāng)然這是已檢查型異常。</p><p> 下面我們來看它在JVM中的運行情況:&l
70、t;/p><p> 圖4-1 輸入正常數(shù)據(jù)時的顏色調(diào)和板</p><p> 當(dāng)我們改變”紅色”值為非法的一個輸入,比如:“AB”時,其運行結(jié)果為如下:</p><p> 圖4-2 輸入一個產(chǎn)生異常的數(shù)據(jù)</p><p> 顏色未變化,好像程序發(fā)生邏輯錯誤似的(沒有按照我們希望的去運行)其實程序已將控制權(quán)轉(zhuǎn)到控制中心去了,這個圖形還處于
71、初始化階段(在輸入“AB”前)。在控制中心我們發(fā)現(xiàn)如下一些異常發(fā)生描述:</p><p> 圖4-3 異常產(chǎn)生后返回到控制臺的說明</p><p> 這說明發(fā)生了NumberFormatException(數(shù)字格式異常)異常后,并沒有繼續(xù)往下執(zhí)行代碼。由于數(shù)字格式異常為已檢查型異常,直接調(diào)用系統(tǒng)的方法處理了。</p><p> JVM報告程序發(fā)生了數(shù)字格式異
72、常產(chǎn)生,并將其具體的情況報告給用戶。但是,是不是這個程序不能再接受正常的輸入數(shù)據(jù)運行呢?還是必須要重新啟動才能運行?</p><p> 我們將“紅色”值由“AB”改為“200”后發(fā)現(xiàn)顏色改變,這說明該程序還能運行。結(jié)果如下圖:</p><p> 圖4-4 將產(chǎn)生異常的輸入數(shù)據(jù)更改后的顏色調(diào)和板</p><p> 可見當(dāng)輸入了正常的數(shù)據(jù)后,程序就像第一次運行一
73、樣,可以正確的顯示顏色了。我們得到的結(jié)論是:部分Java圖形界面程序發(fā)生異常后不需要重新初始化,依舊可以運行,但必須不再有異常發(fā)生。</p><p> 五 Java異常處理基本原則</p><p> 合理使用Java異常機制可以使程序健壯而清晰,但是,Java異常處理機制也常常會被錯誤地使用,下面就討論一些關(guān)于使用異常的原則:</p><p> (一)不要忽
74、略已檢查型異常</p><p><b> 注意下面的代碼:</b></p><p><b> try {</b></p><p> method1(); //method1拋出Exception A</p><p><b> }</b><
75、;/p><p> catch(Exception A) //捕獲Exception A</p><p><b> {</b></p><p> e.rintStackTrace( ); //打印捕獲異常</p><p><b> }</b></p>
76、<p> 上面的代碼似乎沒有什么問題,捕獲異常后將異常打印,然后繼續(xù)執(zhí)行。事實上在catch塊中對發(fā)生的異常情況并沒有作任何處理。雖然程序能夠繼續(xù)執(zhí)行,但是由于這里的操作已經(jīng)發(fā)生異常,將會導(dǎo)致以后的操作不能按照預(yù)期的情況發(fā)展下去,可能導(dǎo)致兩個結(jié)果。</p><p> 一種情況是這里的異常導(dǎo)致在程序中別的地方拋出一個異常,這會使程序員在調(diào)試時感到迷惑,因為新的異常拋出的地方并不是程序真正發(fā)生問題的地方
77、,也不是發(fā)生問題的真正原因。</p><p> 另外一種情況程序繼續(xù)運行,并得出一個錯誤的輸出結(jié)果,這種問題更加難以捕捉,因為很可能把它當(dāng)成一個正確的輸出。</p><p> 那么應(yīng)該如何處理呢?一般有四個選擇:處理異常,進行修復(fù)以讓程序繼續(xù)執(zhí)行;重新拋出異常,在對異常進行分析后發(fā)現(xiàn)這里不能處理它,那么重新拋出異常,讓調(diào)用者處理;將異常轉(zhuǎn)換為用戶可以理解的自定義異常再拋出,這時應(yīng)該注意
78、不要丟失原始異常信息;不要捕獲異常。</p><p> 因此,當(dāng)捕獲一個未檢查型異常時,必須對異常進行處理;如果認為不必要在這里作處理,就不要捕獲該異常,在方法體中聲明方法拋出異常,由上層調(diào)用者來處理該異常。</p><p> (二)不要一次捕獲所有的異常</p><p> 我們常見如下關(guān)于異常處理的代碼:</p><p><b&
79、gt; try</b></p><p><b> {</b></p><p> method1(); //method1拋出ExceptionA</p><p> method2(); //method2拋出ExceptionB</p><p
80、> method3(); //method3拋出ExceptionC</p><p><b> }</b></p><p> catch(Exception e) //捕獲所有異常</p><p><b> { …… }</b></p><p
81、> 代碼中使用一個catch子句捕獲了所有異常,看上去很簡潔,一個代碼段就捕獲了全部的異常。但是這里有兩個潛在的缺陷,一是對try塊中拋出的每種異常,很可能需要不同的處理和恢復(fù)措施,而由于這里只有一個catch塊,分別處理就不能實現(xiàn)。二是try塊中還可能拋出RuntimeException,代碼中捕獲了所有可能拋出的RuntimeException而沒有作任何處理,掩蓋了編程的錯誤,會導(dǎo)致程序難以調(diào)試。</p>&
82、lt;p> 我們應(yīng)該處理為以下的代碼:</p><p><b> try</b></p><p><b> {</b></p><p> method1();</p><p> method2();</p><p> method3();
83、</p><p><b> }</b></p><p> catch (ExceptionA e)//分別捕獲和處理三種異常</p><p><b> { …… }</b></p><p> catch (ExceptionB e)</p><p><
84、b> { …… }</b></p><p> catch (ExceptionC e)</p><p><b> { …… }</b></p><p> (三)使用finally塊釋放資源</p><p> 關(guān)鍵字finally保證程序使用任何方式離開try塊,finally塊中的語句都會被執(zhí)
85、行。當(dāng)程序中使用了外界資源,如數(shù)據(jù)庫連接、文件等,將釋放這些資源的代碼寫入finally塊中是很好的處理方式。</p><p> 必須注意的是,在finally塊中不能拋出異常。Java異常處理機制保證在任何情況下必須先執(zhí)行finally塊然后再離開try塊,因此在try塊中發(fā)生異常的時候,Java虛擬機先轉(zhuǎn)到finally塊執(zhí)行finally塊中的代碼,finally塊執(zhí)行完畢后,再向外拋出異常。如果在fin
86、ally塊中拋出異常,try塊捕捉的異常就不能拋出,外部捕捉到的異常就是finally塊中的異常信息,而try塊中發(fā)生的真正的異常堆棧信息則丟失了。</p><p> Connectioncon = null;</p><p><b> try {</b></p><p> con=dataSource.getConnection();&
87、lt;/p><p><b> //拋出數(shù)據(jù)庫異常</b></p><p><b> }</b></p><p> catch(SQLException e ) {</p><p><b> ……</b></p><p> //捕獲異常進行一些處理后
88、再將數(shù)據(jù)庫異常拋出給調(diào)用者處理</p><p><b> throw e;</b></p><p><b> }</b></p><p><b> finally {</b></p><p><b> try {</b></p>&l
89、t;p> con.close();</p><p><b> }</b></p><p> catch(SQLException e) {</p><p> e.printStackTrace();</p><p><b> ……</b></p><p>&l
90、t;b> }</b></p><p><b> }</b></p><p> 由于con為null,finally塊有異常發(fā)生,從而使try塊中發(fā)生的異常堆棧信息丟失。其調(diào)用者會得到如下信息:</p><p> Java.lang.NullPointerExceptionat</p><p>
91、 myPackage.MyClass.method1(methodl.Java:266)</p><p> (四)異常不能影響對象的狀態(tài)</p><p> 異常產(chǎn)生后不能影響對象的狀態(tài),這是異常處理中的一條重要規(guī)則。一個函數(shù)中發(fā)生異常后,對象的狀態(tài)應(yīng)該和調(diào)用這個函數(shù)之前保持一致,以確保對象處于正確的狀態(tài)中。</p><p> 如果對象是不可變對象(指調(diào)用構(gòu)造函
92、數(shù)創(chuàng)建后就不能改變的對象),即創(chuàng)建后沒有任何方法可以改變對象的狀態(tài),那么異常發(fā)生后對象狀態(tài)肯定不會改變。如果是可變對象,必須在編程中注意保證異常不會影響對象狀態(tài)。</p><p> 有三個方法可以做到異常不能影響對象地狀態(tài):</p><p> ?。?)將可能產(chǎn)生異常的代碼和改變對象狀態(tài)的代碼分開,先執(zhí)行可能產(chǎn)生異常的代碼,如果產(chǎn)生異常,就不執(zhí)行改變對象狀態(tài)的代碼。</p>
93、<p> ?。?)對不容易分離產(chǎn)生異常代碼和改變對象狀態(tài)代碼的方法,定義一個recover方法,在異常產(chǎn)生后調(diào)用recover方法修復(fù)被改變的類變量,恢復(fù)方法調(diào)用前的類狀態(tài)。</p><p> (3)在方法中使用對象的拷貝,這樣當(dāng)異常發(fā)生后,被影響的只是拷貝,對象本身不會受到影響。</p><p> (五)注意丟失(或忽略)的異常</p><p>
94、在程序設(shè)計中,可以丟失(忽略)一些異常,但是為了以后更好地維護代碼,最好不要丟失異常。比如下面的代碼:</p><p> public void method2( ) {</p><p><b> try</b></p><p><b> {</b></p><p><b> …
95、…</b></p><p> method1( ); //method1進行了數(shù)據(jù)庫操作</p><p><b> }</b></p><p> catch( SQLException e )</p><p><b> {</b></p>
96、<p><b> ……</b></p><p> //捕獲數(shù)據(jù)庫異常后將該異常封裝為</p><p> //MyException后重新拋出</p><p> throw new MyException(“發(fā)生了數(shù)據(jù)庫異常:" +e.getMessage);</p><p><b
97、> }</b></p><p><b> }</b></p><p> public void method3( ) {</p><p><b> try</b></p><p><b> {</b></p><p>
98、method2( ); //調(diào)用method2(),拋出MyException</p><p> catch (MyException e)</p><p><b> {</b></p><p> e.printStackTrace( );</p><p><b> ……
99、</b></p><p><b> }</b></p><p><b> }</b></p><p> 在method2的代碼中,try塊捕獲method1拋出的數(shù)據(jù)庫異常SQLException后,拋出了新的自定義異常MyException。這段代碼是似乎沒有什么問題,但在控制臺的輸出卻是:</p
100、><p> MyException:發(fā)生了數(shù)據(jù)庫異常:對象名稱“MyTable"無效</p><p> atMyClass.method2(MyClass.Java:232)</p><p> atMyClass.method3(MyClass.Java:255)</p><p> 原始異常SQLException的信息丟失了,
101、這里只能看到method2里面定義的MyException的堆棧情況,而method1中發(fā)生的數(shù)據(jù)庫異常的堆棧則看不到。</p><p> 如何排錯呢?只有在method1的代碼行中一行行去尋找數(shù)據(jù)庫操作語句。JDK的開發(fā)者們也意識到了這個情況,在JDK1.4.1中,Throwable類增加了兩個構(gòu)造方法,public Throwable ( Throwablecause)和public Throwable (
102、 Stringmessage, Throwablecause ),在構(gòu)造函數(shù)中傳入的原始異常堆棧信息將會在printStackTrace方法中打印出來。但在JDK1.3中就只能靠程序員來實現(xiàn)打印原始異常堆棧信息了。實現(xiàn)過程也很簡單,只需要在自定義的異常類中增加一個原始異常字段,在構(gòu)造函數(shù)中傳入原始異常,然后重載printStackTrace方法,首先調(diào)用類中保存的原始異常的printStackTrace方法,然后再調(diào)用super.pri
103、ntStackTrace方法就可以打印出原始異常信息了??梢赃@樣定義前面代碼中出現(xiàn)的MyException類:</p><p> public class MyException extends Exception</p><p><b> {</b></p><p> public SMException ( Throwable ca
104、use )</p><p><b> //構(gòu)造函數(shù)</b></p><p><b> {</b></p><p> this.cause1=cause;</p><p><b> }</b></p><p> public MyExceptio
105、n ( Strings ,Throwable cause )</p><p><b> {</b></p><p><b> super(s);</b></p><p> this.cause1=cause;</p><p><b> }</b></p>
106、<p> //重載printStackTrace方法,打印出原始異常堆棧信息</p><p> public void printStackTrace()</p><p><b> {</b></p><p> if(cause1!=null)</p><p><b> {</b
107、></p><p> cause1.printStackTrace();</p><p><b> }</b></p><p> super.printStackTrace(s);</p><p><b> }</b></p><p> public voi
108、d printStackTrace(PrintStream s)</p><p><b> {</b></p><p> if(cause1!=null)</p><p><b> {</b></p><p> cause1.printStackTrace(s);</p>
109、<p><b> }</b></p><p> super.printStackTrace(s);</p><p><b> }</b></p><p> public void printStackTrace( PrintWriter s )</p><p><b&g
110、t; {</b></p><p> if(cause1!=null)</p><p><b> {</b></p><p> cause1.printStackTrace(s);</p><p><b> }</b></p><p> super.pr
111、intStackTrace(s);</p><p><b> }</b></p><p> private Throwable cause1;</p><p><b> }</b></p><p> (六)不要同時使用異常機制和返回值來處理異常</p><p>
112、我們可能會使用類似下面一段代碼:</p><p><b> try{</b></p><p> doSomething( );</p><p><b> }</b></p><p> catch(MyException e){</p><p> if(e.getEr
113、rcode==-1)</p><p><b> { …… }</b></p><p> if(e.getErrcode==-2)</p><p><b> { …… }</b></p><p><b> …… </b></p><p> 假如過
114、一段時間再來閱讀這段代碼,你很難弄明白程序的意思?;旌鲜褂肑ava異常處理機制和返回值使程序的異常處理部分變得混亂,并難以理解。</p><p> 在程序中,如果有多種不同的異常情況,就應(yīng)該定義多種不同的異常,而不要像上面代碼那樣。綜合使用Exception和返回值。處理應(yīng)該如下:</p><p><b> try {</b></p><p&g
115、t; doSomething( );</p><p> //doSomething()拋出MyExceptionA和MyExceptionB</p><p><b> }</b></p><p> catch(MyExceptionA e) //捕獲異常分別進行處理</p><p><b>
116、 { …… }</b></p><p> catch(MyExceptionB e)</p><p><b> { …… }</b></p><p> (七)不要讓try塊過于龐大</p><p> 有人習(xí)慣用一個龐大的try塊包含所有可能產(chǎn)生異常的代碼,這樣有兩個壞處:一是閱讀代碼的時候,在try塊
117、冗長的代碼中,不容易知道到底是哪些代碼會拋出哪些異常,不利于代碼維護;二是使用try捕獲異常是以程序執(zhí)行效率為代價的,將不需要捕獲異常的代碼包含在try塊中,影響了代碼執(zhí)行的效率。因此,在Java程序中最好將try塊編寫得簡潔些。</p><p><b> 六 結(jié)束語</b></p><p> Java是一種面向?qū)ο蟮某绦蛟O(shè)計語言,Java的異常處理機制非常出色
118、。Java中的所有異常都是從基礎(chǔ)類Throwable里繼承而來的,所以可確保我們得到的是一個通用接口。丟棄一個錯誤的異常后,Java異常規(guī)范是在編譯期間檢查并執(zhí)行的。被取代的方法必須遵守那一方法的基礎(chǔ)類的異常規(guī)范。</p><p> Java可丟棄指定的異常或者從指定異常衍生出來的其他異常。這樣一來,運用try/catch/finally異常處理機制,最終得到的是更為“健壯”的異常控制代碼。Java異常處理為大
119、的程序項目帶來很好的健壯性。</p><p> 當(dāng)然,在寫本論文時,由于對Java的學(xué)習(xí)沒達到深入、精通程度,掌握Java異常處理技術(shù)也不是很全面,故而論文中對Java圖形程序的異常處理方面和Java異常處理機制具體實現(xiàn)的討論仍有不足之處有待以后改進。</p><p><b> 致謝</b></p><p><b> 參考文獻&
120、lt;/b></p><p> [1]Bloch J.Effective. Java Programming Language Guide.北京:機械工業(yè)出版社,2001.</p><p> [2]Cay S.Horstmann .Core Java 2(第6版) .北京:機械工業(yè)出版社,2004.</p><p> [3]Rogers Cadenhea
121、d ,Laura Lemay.Teach Yourself Java 2 In 21`days.北京:人民郵電出版社,2004.</p><p> [4]朱福喜,唐曉軍.Java程序設(shè)計技巧與實例.北京:人民郵電出版社,2004.</p><p> [5]譚浩強,程龍,楊海蘭,吳功宜.Java編程技術(shù).北京:人民郵電出版社,2003.</p><p> [6]
122、彭晨陽.Java實用系統(tǒng)開發(fā)指南. 北京:機械工業(yè)出版社,2003.</p><p> [7]Java 2 SDK1.5.0 Standard Edition Documentation</p><p> [8]http://Java.sun.com/j2se/index.jsp</p><p> [9]黃聰明.精通Java 2 程序設(shè)計.北京:清華大學(xué)出版社
123、,2004.</p><p> [10]Kalthy Sierra,Bert Bates.Java 2學(xué)習(xí)指南.北京:人民郵電出版社,2004.</p><p> [11]耿祥義.Java基礎(chǔ)教程.北京:清華大學(xué)出版社,2004.</p><p> [12]朱福喜.Java程序設(shè)計技巧與開發(fā)實例.北京:人民郵電出版社,2004.</p><
124、p> [13]謝小樂.J2EE經(jīng)典實例詳解.北京:人民郵電出版社,2003.</p><p> [14]Y.Daniel Liang.Java語言程序設(shè)計(第三版).北京:機械工業(yè)出版社,2005. </p><p> [15]Bruce Eckel.Java編程思想. 北京:機械工業(yè)出版社,2005.</p><p><b> 附
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 眾賞文庫僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 基于java的畢業(yè)設(shè)計論文
- 畢業(yè)設(shè)計(論文)-基于java技術(shù)的論壇(bbs)
- 基于java的酒店管理系統(tǒng)畢業(yè)設(shè)計論文
- 基于java技術(shù)的網(wǎng)上招聘系統(tǒng)的設(shè)計與實現(xiàn)論文(doc畢業(yè)設(shè)計論文)
- 畢業(yè)設(shè)計(論文)--基于java的郵件系統(tǒng)
- 基于java網(wǎng)絡(luò)聊天系統(tǒng)畢業(yè)設(shè)計論文
- 基于java的網(wǎng)上投票系統(tǒng)(doc畢業(yè)設(shè)計論文)
- 優(yōu)秀畢業(yè)設(shè)計論文+源碼基于java的圖書館管理系統(tǒng)畢業(yè)設(shè)計(論文)
- java考試系統(tǒng)畢業(yè)設(shè)計(論文)
- 畢業(yè)設(shè)計--基于java的游戲設(shè)計
- java坦克大戰(zhàn)畢業(yè)設(shè)計論文
- 基于java的論壇bbs的實現(xiàn)(doc畢業(yè)設(shè)計論文)
- 基于java的cs模式網(wǎng)絡(luò)聊天室的畢業(yè)設(shè)計(doc畢業(yè)設(shè)計論文)
- 基于java的圖書館管理系統(tǒng)畢業(yè)設(shè)計(論文)
- 畢業(yè)設(shè)計(論文)+手機游戲開發(fā)-java畢業(yè)論文
- 基于java手機游戲畢業(yè)設(shè)計
- java畢業(yè)設(shè)計論文 網(wǎng)上購物書店設(shè)計
- java學(xué)生管理系統(tǒng)畢業(yè)設(shè)計論文
- java畢業(yè)設(shè)計論文中期報告
- 畢業(yè)設(shè)計論文 java 24點游戲設(shè)計
評論
0/150
提交評論