您的位置:首页 > 其它

Error Handling with Exceptions【3】

2007-11-16 17:28 260 查看
importcom.bruceeckel.simpletest.*;


classDynamicFieldsExceptionextendsException{}


publicclassDynamicFields{

privatestaticTestmonitor=newTest();

privateObject[][]fields;

publicDynamicFields(intinitialSize){

fields=newObject[initialSize][2];

for(inti=0;i<initialSize;i++)

fields[i]=newObject[]{null,null};

}

publicStringtoString(){

StringBufferresult=newStringBuffer();

for(inti=0;i<fields.length;i++){

result.append(fields[i][0]);

result.append(":");

result.append(fields[i][1]);

result.append("/n");

}

returnresult.toString();

}

privateinthasField(Stringid){

for(inti=0;i<fields.length;i++)

if(id.equals(fields[i][0]))

returni;

return-1;

}

privateintgetFieldNumber(Stringid)throwsNoSuchFieldException{

intfieldNum=hasField(id);

if(fieldNum==-1)

thrownewNoSuchFieldException();

returnfieldNum;

}

privateintmakeField(Stringid){

for(inti=0;i<fields.length;i++)

if(fields[i][0]==null){

fields[i][0]=id;

returni;

}

//Noemptyfields.Addone:

Object[][]tmp=newObject[fields.length+1][2];

for(inti=0;i<fields.length;i++)

tmp[i]=fields[i];

for(inti=fields.length;i<tmp.length;i++)

tmp[i]=newObject[]{null,null};

fields=tmp;

//Reursivecallwithexpandedfields:

returnmakeField(id);

}

publicObjectgetField(Stringid)throwsNoSuchFieldException{

returnfields[getFieldNumber(id)][1];

}

publicObjectsetField(Stringid,Objectvalue)

throwsDynamicFieldsException{

if(value==null){

//Mostexceptionsdon'thavea"cause"constructor.

//InthesecasesyoumustuseinitCause(),

//availableinallThrowablesubclasses.

DynamicFieldsExceptiondfe=

newDynamicFieldsException();

dfe.initCause(newNullPointerException());

throwdfe;

}

intfieldNumber=hasField(id);

if(fieldNumber==-1)

fieldNumber=makeField(id);

Objectresult=null;

try{

result=getField(id);//Getoldvalue

}catch(NoSuchFieldExceptione){

//Useconstructorthattakes"cause":

thrownewRuntimeException(e);

}

fields[fieldNumber][1]=value;

returnresult;

}

publicstaticvoidmain(String[]args){

DynamicFieldsdf=newDynamicFields(3);

System.out.println(df);

try{

df.setField("d","Avalueford");

df.setField("number",newInteger(47));

df.setField("number2",newInteger(48));

System.out.println(df);

df.setField("d","Anewvalueford");

df.setField("number3",newInteger(11));

System.out.println(df);

System.out.println(df.getField("d"));

Objectfield=df.getField("a3");//Exception

}catch(NoSuchFieldExceptione){

thrownewRuntimeException(e);

}catch(DynamicFieldsExceptione){

thrownewRuntimeException(e);

}

monitor.expect(newString[]{

"null:null",

"null:null",

"null:null",

"",

"d:Avalueford",

"number:47",

"number2:48",

"",

"d:Anewvalueford",

"number:47",

"number2:48",

"number3:11",

"",

"Avalueford",

"Exceptioninthread/"main/""+

"java.lang.RuntimeException:"+

"java.lang.NoSuchFieldException",

"/tatDynamicFields.main(DynamicFields.java:98)",

"Causedby:java.lang.NoSuchFieldException",

"/tatDynamicFields.getFieldNumber("+

"DynamicFields.java:37)",

"/tatDynamicFields.getField(DynamicFields.java:58)",

"/tatDynamicFields.main(DynamicFields.java:96)"

});

}

}

EachDynamicFieldsobjectcontainsanarrayofObject-Objectpairs.Thefirstobjectisthefieldidentifier(aString),andthesecondisthefieldvalue,whichcanbeanytypeexceptanunwrappedprimitive.Whenyoucreatetheobject,youmakeaneducatedguessabouthowmanyfieldsyouneed.WhenyoucallsetField(),iteitherfindstheexistingfieldbythatnameorcreatesanewone,andputsinyourvalue.Ifitrunsoutofspace,itaddsnewspacebycreatinganarrayoflengthonelongerandcopyingtheoldelementsin.Ifyoutrytoputinanullvalue,thenitthrowsaDynamicFieldsExceptionbycreatingoneandusinginitCause()toinsertaNullPointerExceptionasthecause.

每个DynamicFields对象都包含一个Object-Object的数组对,第一个对象是标识符为字符串类型,第二个对象是数据项,可以是除了基本类型之外的所有类型。当你创建一个对象的时候,你估计一下需要多少数据成员,你可以调用setField(),这个方法可以通过标识符查找数据项是否存在或者创建一个新的数据项并且给value赋值。如果控件用完了的话,它会创建一个比原来的数组多一个元素的数组,并且把原来的数据拷贝到新的数组去,如果你试图赋一个为null的值,它会创建一个DynamicFieldsException对象并抛出该异常,并且使用NullPointerException来作为cause插进去。

Asareturnvalue,setField()alsofetchesouttheoldvalueatthatfieldlocationusinggetField(),whichcouldthrowaNoSuchFieldException.IftheclientprogrammercallsgetField(),thentheyareresponsibleforhandlingNoSuchFieldException,butifthisexceptionisthrowninsidesetField(),it’saprogrammingerror,sotheNoSuchFieldExceptionisconvertedtoaRuntimeExceptionusingtheconstructorthattakesacauseargument.

作为返回值,setField()会调用getField()提取数据成员值并返回,但是可能会抛出一个NoSuchFieldException异常信息,如果客户端程序员调用getField()方法,那么他们必须要处理NoSuchFieldException异常信息,但是如果这个异常信息是setField()抛出来的话,这就是一个变成错误了,因为NoSuchFieldException异常会拿cause当作参数的构造方法转化成RunTimeException异常。

StandardJavaexceptions

TheJavaclassThrowabledescribesanythingthatcanbethrownasanexception.TherearetwogeneraltypesofThrowableobjects(“typesof”=“inheritedfrom”).Errorrepresentscompile-timeandsystemerrorsthatyoudon’tworryaboutcatching(exceptinspecialcases).ExceptionisthebasictypethatcanbethrownfromanyofthestandardJavalibraryclassmethodsandfromyourmethodsandrun-timeaccidents.SotheJavaprogrammer’sbasetypeofinterestisusuallyException.

Java的Throwable类描述了作为异常可以抛出的所有信息,这里有两个自Throwable继承下来的类。一个是Error,它是用来描述编译期间系统错误的,不需要捕获(特殊情况除外)。还有一个就是Exception,这是一个可以被任何一个Java标准类库中的方法抛出来,或者在你自己的类中运行期间的错误也可以抛出该异常。所以Java的程序员选择异常基类的时候更倾向于Exception。

ThebestwaytogetanoverviewoftheexceptionsistobrowsetheHTMLJavadocumentationthatyoucandownloadfromjava.sun.com.It’sworthdoingthisoncejusttogetafeelforthevariousexceptions,butyou’llsoonseethatthereisn’tanythingspecialbetweenoneexceptionandthenextexceptforthename.Also,thenumberofexceptionsinJavakeepsexpanding;basically,it’spointlesstoprinttheminabook.Anynewlibraryyougetfromathird-partyvendorwillprobablyhaveitsownexceptionsaswell.Theimportantthingtounderstandistheconceptandwhatyoushoulddowiththeexceptions.

了解Java的异常最好的办法就是浏览HTML版本的Java帮助文档,你可以在java.sun.com下载,看一遍还是值得的,这样可以对各种异常信息有一个感性的认识,但是你很快就会发现两个异常之间除了名字之外几乎没有什么不同之处,此外Java异常的数量还在不断的增加;但是在本书中罗列这些也没有什么意义,而且你拿到的第三方的类库一般额都会提供一套自己的异常机制,其实最重要的是理解异常的概念和知道应该怎样操作异常。

Thebasicideaisthatthenameoftheexceptionrepresentstheproblemthatoccurred,andtheexceptionnameisintendedtoberelativelyself-explanatory.Theexceptionsarenotalldefinedinjava.lang;somearecreatedtosupportotherlibrariessuchasutil,net,andio,whichyoucanseefromtheirfullclassnamesorwhattheyareinheritedfrom.Forexample,allI/Oexceptionsareinheritedfromjava.io.IOException.

基本的原则就是使用异常的名字来标识遇到了什么问题,异常的名字尽量是自解释的。并不是所有的异常都定义在java.lang;有一些是为了支持其它的类库而创建的例如util、net和io,你可用从类的全名或者所继承的基类可以看出一些端倪,所有I/O的异常都是继承自java.io.IOException。

ThespecialcaseofRuntimeException

Thefirstexampleinthischapterwas

if(t==null)

thrownewNullPointerException();

Itcanbeabithorrifyingtothinkthatyoumustcheckfornulloneveryreferencethatispassedintoamethod(sinceyoucan’tknowifthecallerhaspassedyouavalidreference).Fortunately,youdon’t—thisispartofthestandardrun-timecheckingthatJavaperformsforyou,andifanycallismadetoanullreference,JavawillautomaticallythrowaNullPointerException.Sotheabovebitofcodeisalwayssuperfluous.

如果需要校验传递给你的每个方法的参数是否为null确实是比较恶心,因为你不知道调用者传递给你的是否是一个有效的reference,幸运的是其实不需要,因为这是标准的运行时检查的一部分,Java会替你做,如果某个调用者传入了null,Java会自动的抛出一个NullPointerExcepion。所以上面这部分代码就是多余的了。

There’sawholegroupofexceptiontypesthatareinthiscategory.They’realwaysthrownautomaticallybyJavaandyoudon’tneedtoincludetheminyourexceptionspecifications.Convenientlyenough,they’reallgroupedtogetherbyputtingthemunderasinglebaseclasscalledRuntimeException,whichisaperfectexampleofinheritance;Itestablishesafamilyoftypesthathavesomecharacteristicsandbehaviorsincommon.Also,youneverneedtowriteanexceptionspecificationsayingthatamethodmightthrowaRuntimeException(oranytypeinheritedfromRuntimeException),becausetheyareuncheckedexceptions.Becausetheyindicatebugs,youdon’tusuallycatchaRuntimeException—it’sdealtwithautomatically.IfyouwereforcedtocheckforRuntimeExceptions,yourcodecouldgettoomessy.Eventhoughyoudon’ttypicallycatchRuntimeExceptions,inyourownpackagesyoumightchoosetothrowsomeoftheRuntimeExceptions.

在NullPointerException异常类中包含了很多的异常类型,他们通常都是Java自动抛出的不需要在异常处理程序中捕获它们。它们都是继承的RuntimeException基类,所以使用起来方便了很多,而且这也是一个很好的展现继承的例子。它建立了一个具有相同特征和行为的类系。当然你也不需要去为这里面的方法作一个异常处理程序,怀疑它会抛出一个RuntimeException异常,因为它们都是UncheckedExceptions。由于它们表示Bugs,所以你不需要去捕获它们,它会自动的处理。如果你非要去检查RuntimeException那么你的代码会很凌乱,虽然一般你不回去捕捉这些RuntimeException,但是在你自己创建package的时候可能会抛出RuntimeException异常信息。

Whathappenswhenyoudon’tcatchsuchexceptions?Sincethecompilerdoesn’tenforceexceptionspecificationsforthese,it’squiteplausiblethataRuntimeExceptioncouldpercolateallthewayouttoyourmain()methodwithoutbeingcaught.Toseewhathappensinthiscase,trythefollowingexample:

你不捕捉这些异常又会发生些什么呢?因为编译器不会强制你为这些作异常处理程序,所以RuntimeException应会不受到任何阻挡直接到达main()方法,如果想试试这样的话会发生什么试试下面的代码:

importcom.bruceeckel.simpletest.*;


publicclassNeverCaught{

privatestaticTestmonitor=newTest();

staticvoidf(){

thrownewRuntimeException("Fromf()");

}

staticvoidg(){

f();

}

publicstaticvoidmain(String[]args){

g();

monitor.expect(newString[]{

"Exceptioninthread/"main/""+

"java.lang.RuntimeException:Fromf()",

"atNeverCaught.f(NeverCaught.java:7)",

"atNeverCaught.g(NeverCaught.java:10)",

"atNeverCaught.main(NeverCaught.java:13)"

});

}

}

YoucanalreadyseethataRuntimeException(oranythinginheritedfromit)isaspecialcase,sincethecompilerdoesn’trequireanexceptionspecificationforthesetypes.

你可以看到RuntimeException或者它的派生类是个特例,因为编译器不需要为它们作异常处理。

Sotheansweris:IfaRuntimeExceptiongetsallthewayouttomain()withoutbeingcaught,printStackTrace()iscalledforthatexceptionastheprogramexits.

所以答案就是:如果RuntimeException异常没有被捕获而是一路到达了main()方法,那么在程序退出的时候会为这个异常信息调用printStackTrace()方法。

KeepinmindthatyoucanonlyignoreexceptionsoftypeRuntimeException(andsubclasses)inyourcoding,sinceallotherhandlingiscarefullyenforcedbythecompiler.ThereasoningisthataRuntimeExceptionrepresentsaprogrammingerror:

记在心里,你可以在代码中忽略RuntimeException(以及它的子类),因为编译器强制要求你处理其它的异常。原因就是RuntimeException描述的是程序错误。

Anerroryoucannotanticipate.Forexample,anullreferencethatisoutsideofyourcontrol.

Anerrorthatyou,asaprogrammer,shouldhavecheckedforinyourcode(suchasArrayIndexOutOfBoundsExceptionwhereyoushouldhavepaidattentiontothesizeofthearray).Anexceptionthathappensfrompoint#1oftenbecomesanissueforpoint#2.

1.错误是不可预料的,例如一个null的reference就不在你的控制范围内;
2.针对错误,作为一个程序员你应该检查你的代码(诸如ArrayIndexOutOfBoundsException你应当把精力集中到array的长度上)。第一种情况发生的异常经常演化成第二种情况发生的异常。
Youcanseewhatatremendousbenefititistohaveexceptionsinthiscase,sincetheyhelpinthedebuggingprocess.

It’sinterestingtonoticethatyoucannotclassifyJavaexceptionhandlingasasingle-purposetool.Yes,itisdesignedtohandlethosepeskyrun-timeerrorsthatwilloccurbecauseofforcesoutsideyourcode’scontrol,butit’salsoessentialforcertaintypesofprogrammingbugsthatthecompilercannotdetect.

你可用看到这种情况下有异常处理是有很大好处的,因为在调试当中会给你很大的帮助。值得一提的是你不要仅仅的把Java的异常处理程序作为一种单一的工具。它设计的目的是为了那些不在你控制之下的因素,会引起的运行时的错误,它们对于那些编译器无法发现的一些Bug的类型也是很有必要的。

Performingcleanupwithfinally

There’softensomepieceofcodethatyouwanttoexecutewhetherornotanexceptionisthrownwithinatryblock.Thisusuallypertainstosomeoperationotherthanmemoryrecovery(sincethat’stakencareofbythegarbagecollector).Toachievethiseffect,youuseafinallyclause[41]attheendofalltheexceptionhandlers.Thefullpictureofanexceptionhandlingsectionisthus:

我们经常遇到这样的情况,不管异常在try区是否抛出了,我们都希望一部分代码都要执行。这经常被用来作除了恢复内存之外的一些操作(因为内存回收是由垃圾回收器作的),要实现这种效果你可以在所有的异常处理程序之后使用finally句子。这样一个完整的异常处理程序就是这样的了:

try{

//Theguardedregion:Dangerousactivities

//thatmightthrowA,B,orC

}catch(Aa1){

//HandlerforsituationA

}catch(Bb1){

//HandlerforsituationB

}catch(Cc1){

//HandlerforsituationC

}finally{

//Activitiesthathappeneverytime

}

Todemonstratethatthefinallyclausealwaysruns,trythisprogram:

为了证明finally句子使用会执行,试试下面的程序:

importcom.bruceeckel.simpletest.*;


classThreeExceptionextendsException{}


publicclassFinallyWorks{

privatestaticTestmonitor=newTest();

staticintcount=0;

publicstaticvoidmain(String[]args){

while(true){

try{

if(count++==0)

thrownewThreeException();

System.out.println("Noexception");

}catch(ThreeExceptione){

System.err.println("ThreeException");

}finally{

System.err.println("Infinallyclause");

if(count==2)break;

}

}

monitor.expect(newString[]{

"ThreeException",

"Infinallyclause",

"Noexception",

"Infinallyclause"

});

}

}

Fromtheoutput,youcanseethatwhetherornotanexceptionisthrown,thefinallyclauseisalwaysexecuted.

通过输出结果你可以看到不管是否抛出了异常信息finally句子总是会被执行。

ThisprogramalsogivesahintforhowyoucandealwiththefactthatexceptionsinJava(likeexceptionsinC++)donotallowyoutoresumebacktowheretheexceptionwasthrown,asdiscussedearlier.Ifyouplaceyourtryblockinaloop,youcanestablishaconditionthatmustbemetbeforeyoucontinuetheprogram.Youcanalsoaddastaticcounterorsomeotherdevicetoallowthelooptotryseveraldifferentapproachesbeforegivingup.Thiswayyoucanbuildagreaterlevelofrobustnessintoyourprograms.

这个程序也给了我们一些提示:我们如何应对在Java中发生了异常后会阻止我们回到异常的抛出地点,如果你在try区放入一个循环,你可以建立一个条件必须成立后才可以继续执行后面的程序,你也可以增加一个计数器或者其它的方式让循环多以几种不同的方式去试,知道放弃为止。这样你就可以让你的程序的健壮性更上一个台阶。

What’sfinallyfor?

Inalanguagewithoutgarbagecollectionandwithoutautomaticdestructorcalls,[42]finallyisimportantbecauseitallowstheprogrammertoguaranteethereleaseofmemoryregardlessofwhathappensinthetryblock.ButJavahasgarbagecollection,soreleasingmemoryisvirtuallyneveraproblem.Also,ithasnodestructorstocall.SowhendoyouneedtousefinallyinJava?

在没有垃圾回收器和自动析构方法的语言中,finally是非常重要的,因为它允许程序员来保证不管在try区发生了什么都可以去释放内存控件。但是Java提供了垃圾回收器,所以释放内存就不是问题了,当然它没有析构方法去供调用。所以在Java中你什么时候才需要finally呢?

Thefinallyclauseisnecessarywhenyouneedtosetsomethingotherthanmemorybacktoitsoriginalstate.Thisissomekindofcleanuplikeanopenfileornetworkconnection,somethingyou’vedrawnonthescreen,orevenaswitchintheoutsideworld,asmodeledinthefollowingexample:

当你需要把一些除了内存之外的一些东西回退到原始的状态时,这finally句子还是很有用的。可能是一个打开的文件或者网络连接,或者你画在屏幕上的一些东西,甚至是现实世界中的开关,就像下面的例子中示范的:

publicclassSwitch{

privatebooleanstate=false;

publicbooleanread(){returnstate;}

publicvoidon(){state=true;}

publicvoidoff(){state=false;}

}


publicclassOnOffException1extendsException{}


publicclassOnOffException2extendsException{}


publicclassOnOffSwitch{

privatestaticSwitchsw=newSwitch();

publicstaticvoidf()

throwsOnOffException1,OnOffException2{}

publicstaticvoidmain(String[]args){

try{

sw.on();

f();

sw.off();

}catch(OnOffException1e){

System.err.println("OnOffException1");

sw.off();

}catch(OnOffException2e){

System.err.println("OnOffException2");

sw.off();

}

}

}

Thegoalhereistomakesurethattheswitchisoffwhenmain()iscompleted,sosw.off()isplacedattheendofthetryblockandattheendofeachexceptionhandler.Butit’spossiblethatanexceptioncouldbethrownthatisn’tcaughthere,sosw.off()wouldbemissed.However,withfinallyyoucanplacethecleanupcodefromatryblockinjustoneplace:

这里的目的就是确保在main()方法执行完毕之后switch是关闭的,所以sw.off()被写在了try区的后面也写在了每个异常处理程序中。但是很有可能抛出了一个你捕获不到的异常,所以sw.off()可能会执行不到。然而你如果使用finally的话你只需要在try的一个位置写就可以了。

publicclassWithFinally{

staticSwitchsw=newSwitch();

publicstaticvoidmain(String[]args){

try{

sw.on();

OnOffSwitch.f();

}catch(OnOffException1e){

System.err.println("OnOffException1");

}catch(OnOffException2e){

System.err.println("OnOffException2");

}finally{

sw.off();

}

}

}

Herethesw.off()hasbeenmovedtojustoneplace,whereit’sguaranteedtorunnomatterwhathappens.

这里的sw.off()被移动到了一个位置,这里能够确保不管发生什么都会被执行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: