\"異常\"指的是程序運(yùn)行時(shí)出現(xiàn)的非正常情況。在用傳統(tǒng)的語(yǔ)言編程時(shí),程序員只能通過(guò)函數(shù)的返回值來(lái)發(fā)出錯(cuò)誤信息。這易于導(dǎo)致很多錯(cuò)誤,因?yàn)樵诤芏嗲闆r下需要知道錯(cuò)誤產(chǎn)生的內(nèi)部細(xì)節(jié)。通常,用全局變量errno來(lái)存儲(chǔ)\"異常\"的類(lèi)型。這容易導(dǎo)致誤用,因?yàn)橐粋€(gè)errno的值有可能在被處理?reg;前被另外的錯(cuò)誤覆蓋掉。即使美的C語(yǔ)言程序,為了處理\"異常\"情況,也常求助于goto語(yǔ)句。Java對(duì)\"異常\"的處理是面向?qū)ο蟮?。一個(gè)Java的Exception是一個(gè)描述\"異常\"情況的對(duì)象。當(dāng)出現(xiàn)\"異常\"情況時(shí),一個(gè)Exception對(duì)象就產(chǎn)生了,并放到產(chǎn)生這個(gè)\"異常\"的成員函數(shù)里。
8.1基礎(chǔ)
Java的\"異常\"處理是通過(guò)5個(gè)關(guān)鍵詞來(lái)實(shí)現(xiàn)的:try,catch,throw,throws和finally。用try來(lái)執(zhí)行一段程序,如果出現(xiàn)\"異常\",系統(tǒng)拋出(throws?copy;一個(gè)\"異常\",你可以通過(guò)它的類(lèi)型來(lái)捕捉(catch?copy;它,或最后(finally?copy;由缺省處理器來(lái)處理。下面是\"異常\"處理程序的基本形式:try{//程序塊}catch(ExceptionType1e){//對(duì)ExceptionType1的處理}catch(ExceptionType2e){//對(duì)ExceptionType2的處理throw(e);//再拋出這個(gè)\"異常\"}finally{}
8.2\"異常\"的類(lèi)型
在\"異常\"類(lèi)層次的最上層有一個(gè)單獨(dú)的類(lèi)叫做Throwable。這個(gè)類(lèi)用來(lái)表示所有的\"異常\"情況。每個(gè)\"異常\"類(lèi)型都是Throwable的子類(lèi)。Throwable有兩個(gè)直接的子類(lèi)。一類(lèi)是Exception,是用戶程序能夠捕捉到的\"異常\"情況。我們將通過(guò)產(chǎn)生它的子類(lèi)來(lái)創(chuàng)建自己的\"異常\"。另一類(lèi)是Error,它定義了那?copy;通常無(wú)法捕捉到的\"異常\"。要謹(jǐn)慎使用Error子類(lèi),因?yàn)樗鼈兺ǔ?huì)導(dǎo)致災(zāi)難性的失敗。在Exception中有一個(gè)子類(lèi)RuntimeException,它是程序運(yùn)行時(shí)自動(dòng)地對(duì)某?copy;錯(cuò)誤作出反應(yīng)而產(chǎn)生的。
8.3不捕捉\"異常\"
\"異常\"對(duì)象是Java在運(yùn)行時(shí)對(duì)某?copy;\"異常\"情況作出反應(yīng)而產(chǎn)生的。例如,下面這個(gè)小程序包含一個(gè)整數(shù)被0除的\"異常\"。
classExc0{publicstaticvoidmain(Stringargs[]){intd=0;inta=42/d;}}
當(dāng)Java執(zhí)行這個(gè)除法時(shí),由于分母是0,就會(huì)構(gòu)造一個(gè)\"異常\"對(duì)象來(lái)使程序停下來(lái)并處理這個(gè)錯(cuò)誤情況,在運(yùn)行時(shí)\"拋出\"(throw?copy;這個(gè)\"異常\"。說(shuō)\"拋出\"是因?yàn)樗笠粋€(gè)滾燙的馬鈴薯,你必須把它抓住并立即處理。程序流將會(huì)在除號(hào)操作符處被打斷,然后檢查當(dāng)前的調(diào)用堆棧來(lái)查找\"異常\"。一個(gè)\"異常\"處理器是用來(lái)立即處理\"異常\"情況的。在這個(gè)例子里,我們沒(méi)有編一個(gè)\"異常\"處理器,所以缺省的處理器就發(fā)揮作用了。缺省的處理器打印Exception的字符?reg;值和發(fā)生\"異常\"的地點(diǎn)。
下面是我們的小例子的輸出。
C:\\>javaExc0java.lang.arithmeticException:/byzeroatExc0.main(Exc0.java:4)
8.4try與catch
通常我們希望自己來(lái)處理\"異常\"并繼續(xù)運(yùn)行??梢杂胻ry來(lái)指定一塊預(yù)防所有\(zhòng)"異常\"的的程序。緊跟在try程序后面,應(yīng)包含一個(gè)catch子句來(lái)指定你想要捕捉的\"異常\"的類(lèi)型。例如,下面的例子是在前面的例子的基礎(chǔ)上構(gòu)造的,但它包含一個(gè)try程序塊和一個(gè)catch子句。classexc1{publicstaticvoidmain(stringargs[]){try{intd=0;inta=42/d;}catch(arithmeticexceptione){system.out.println(\"divisionbyzero\");}}}
catch子句的目標(biāo)是解決\"異常\"情況,把變量設(shè)到合理的狀態(tài),并象沒(méi)有出錯(cuò)一樣繼續(xù)運(yùn)行。如果一個(gè)子程序不處理某個(gè)\"異常\",則返到上一級(jí)處理,直到最外一級(jí)。
8.5多個(gè)catch子句
在某情況下,同一段程序可能產(chǎn)生不止一種\"異常\"情況。你可以放置多個(gè)catch子句,其中每一種\"異常\"類(lèi)型都將被檢查,第一個(gè)與?reg;匹配的就會(huì)被執(zhí)行。如果一個(gè)類(lèi)和其子類(lèi)都有的話,應(yīng)把子類(lèi)放在前面,否則將永遠(yuǎn)不會(huì)到達(dá)子類(lèi)。下面是一個(gè)有兩個(gè)catch子句的程序的例子。
classMultiCatch{publicstaticvoidmain(Stringargs[]){try{inta
=args.length;System.out.println(\"a=\"+a);intb=42/a;intc[]=
{1};c[42]=99;}catch(ArithmeticExceptione){System.out.println(\"div
by0:\"+e);}catch(ArrayIndexOutOfBoundsExceptione)
{system.out.println(\"arrayindexoob:\"+e);}}}
如果在程序運(yùn)行時(shí)不跟參數(shù),將會(huì)引起一個(gè)0做除數(shù)的\"異常\",因?yàn)閍的值為0。如果我們提?copy;一個(gè)命令行參數(shù),將不會(huì)產(chǎn)生這個(gè)\"異常\",因?yàn)閍的值大于0。但會(huì)引起一個(gè)ArrayIndexOutOfBoundexception的\"異常\",因?yàn)檎蛿?shù)組c的長(zhǎng)度是1,卻給c[42]賦值。下面是以上兩種情況的運(yùn)行結(jié)果。
C:\\>javaMultiCatcha=0divby0:java.lang.arithmeticexception:/by
zeroC:\\>javaMutiCatch1a=1arrayindexoob:
java.lang.ArrayIndexOutOfBoundsException:42
8.6try語(yǔ)句的嵌套
你可以在一個(gè)成員函數(shù)調(diào)用的外面寫(xiě)一個(gè)try語(yǔ)句,在這個(gè)成員函數(shù)內(nèi)部,寫(xiě)另一個(gè)try語(yǔ)句保護(hù)其他代碼。每當(dāng)遇到一個(gè)try語(yǔ)句,\"異常\"的框架就放到堆棧上面,直到所有的try語(yǔ)句都完成。如果下一級(jí)的try語(yǔ)句沒(méi)有對(duì)某種\"異常\"進(jìn)行處理,堆棧就會(huì)展開(kāi),直到遇到有處理這種\"異常\"的try語(yǔ)句。下面是一個(gè)try語(yǔ)句嵌套的例子。
classMultiNest{staticvoidprocedure(){try{intc[]={1}:c[42]
=99;}catch(ArrayIndexOutOfBoundsexceptione)
{System.out.println(\"arrayindexoob:\"+e);}}publicstaticvoid
main(Stringargs[]){try{inta=args.length;system.out.println(\"a
=\"+a);intb=42/a;procedure();}catch(arithmeticExceptione)
{System.out.println(\"divby0:\"+e);}}}
成員函數(shù)procedure里有自己的try/catch控制,所以main不用去處理ArrayIndexOutOfBoundsException。
8.7throw語(yǔ)句
throw語(yǔ)句用來(lái)明確地拋出一個(gè)\"異常\"。首先,你必須得到一個(gè)Throwable的實(shí)例的控制柄,通過(guò)參數(shù)傳到catch子句,或者用new操作符來(lái)創(chuàng)建一個(gè)。下面是throw語(yǔ)句的通常形式。
throwThrowableInstance;
程序會(huì)在throw語(yǔ)句后立即終止,它后面的語(yǔ)句執(zhí)行不到,然后在包含它的所有try塊中從里向外尋找含有與其匹配的catch子句的try塊。下面是一個(gè)含有throw語(yǔ)句的例子。
classThrowDemo{staticvoiddemoproc(){try{thrownewNullPointerException(\"de3mo\");}catch(NullPointerExceptione){System.out.println(\"caughtinsidedemoproc\");throwe;}}publicstaticvoidmain(Stringargs[]){try{demoproc();}
catch(NullPointerExceptione){system.out.println(\"recaught:\"+e);}}}
8.8throws語(yǔ)句
throws用來(lái)標(biāo)明一個(gè)成員函數(shù)可能拋出的各種\"異常\"。對(duì)大多數(shù)Exception子類(lèi)來(lái)說(shuō),Java編譯器會(huì)強(qiáng)迫你聲明在一個(gè)成員函數(shù)中拋出的\"異常\"的類(lèi)型。如果\"異常\"的類(lèi)型是Error或RuntimeException,或它們的子類(lèi),這個(gè)規(guī)則不起作用,因?yàn)檫@?copy;在程序的正常部分中是不期待出現(xiàn)的。如果你想明確地拋出一個(gè)RuntimeException,你必須用throws語(yǔ)句來(lái)聲明它的類(lèi)型。這就重新定義了成員函數(shù)
的定義語(yǔ)法:typemethod-name(arg-list)throwsexception-list{}
下面是一段程序,它拋出了一個(gè)\"異常\",但既沒(méi)有捕捉它,也沒(méi)有用throws來(lái)聲明。這在編譯時(shí)將不會(huì)通過(guò)。
classThrowsDemo1{staticvoidprocedure()[System.out.println(\"inside
procedure\");thrownewIllegalAccessException(\"demo\");}publicstatic
voidmain(Stringargs[]){procedure();}}
為了讓這個(gè)例子編譯過(guò)去,我們需要聲明成員函數(shù)procedure拋出了IllegalAccessException,并且在調(diào)用它的成員函數(shù)main里捕捉它。下面是正確的例子:
classThrowsDemo{staticvoidprocedure()throwsIllegalAccessException
{System.out.println(\"insideprocedure\");thrownew
IllegalAccessException(\"demo\");}publicstaticvoidmain(Stringargs[])
{try{procedure();}catch(IllegalAccessExceptione)
{System.out.println(\"caught\"+e);}}}
下面是輸出結(jié)果:
C:\\>javaThrowsDemoinsideprocedurecaught
java.lang.IllegalAccessException:demo

