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