关于Java中finally语句块的深度辨析.docx

上传人:b****1 文档编号:2601899 上传时间:2023-05-04 格式:DOCX 页数:20 大小:71.40KB
下载 相关 举报
关于Java中finally语句块的深度辨析.docx_第1页
第1页 / 共20页
关于Java中finally语句块的深度辨析.docx_第2页
第2页 / 共20页
关于Java中finally语句块的深度辨析.docx_第3页
第3页 / 共20页
关于Java中finally语句块的深度辨析.docx_第4页
第4页 / 共20页
关于Java中finally语句块的深度辨析.docx_第5页
第5页 / 共20页
关于Java中finally语句块的深度辨析.docx_第6页
第6页 / 共20页
关于Java中finally语句块的深度辨析.docx_第7页
第7页 / 共20页
关于Java中finally语句块的深度辨析.docx_第8页
第8页 / 共20页
关于Java中finally语句块的深度辨析.docx_第9页
第9页 / 共20页
关于Java中finally语句块的深度辨析.docx_第10页
第10页 / 共20页
关于Java中finally语句块的深度辨析.docx_第11页
第11页 / 共20页
关于Java中finally语句块的深度辨析.docx_第12页
第12页 / 共20页
关于Java中finally语句块的深度辨析.docx_第13页
第13页 / 共20页
关于Java中finally语句块的深度辨析.docx_第14页
第14页 / 共20页
关于Java中finally语句块的深度辨析.docx_第15页
第15页 / 共20页
关于Java中finally语句块的深度辨析.docx_第16页
第16页 / 共20页
关于Java中finally语句块的深度辨析.docx_第17页
第17页 / 共20页
关于Java中finally语句块的深度辨析.docx_第18页
第18页 / 共20页
关于Java中finally语句块的深度辨析.docx_第19页
第19页 / 共20页
关于Java中finally语句块的深度辨析.docx_第20页
第20页 / 共20页
亲,该文档总共20页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

关于Java中finally语句块的深度辨析.docx

《关于Java中finally语句块的深度辨析.docx》由会员分享,可在线阅读,更多相关《关于Java中finally语句块的深度辨析.docx(20页珍藏版)》请在冰点文库上搜索。

关于Java中finally语句块的深度辨析.docx

关于Java中finally语句块的深度辨析

关于Java中finally语句块的深度辨析

乍看这个题目,是不是有人会问,这个谁不知道啊,大凡熟悉Java编程的人都知道finally语句块的作用和用法。

有什么可深度辨析的呢?

事实并非如此,我发现即使写了很多年Java程序的人,也不一定能够透彻的理解finally语句块。

本篇将以生动形象的案例来带您由浅入深的来分析一下这个小小的finally,希望这篇文章能够让您真正的理解finally语句块的本质,至少阅读完本篇文章后,没有觉得浪费了时间。

可不能小看这个简单的finally,看似简单的问题背后,却隐藏了无数的玄机。

接下来我就带您一步一步的揭开这个finally的神秘面纱。

问题分析

首先来问大家一个问题:

finally语句块一定会执行吗?

很多人都认为finally语句块是肯定要执行的,其中也包括一些很有经验的Java程序员。

可惜并不像大多人所认为的那样,对于这个问题,答案当然是否定的,我们先来看下面这个例子。

清单1.

publicclassTest{

publicstaticvoidmain(String[]args){

System.out.println("returnvalueoftest():

"+test());

}

publicstaticinttest(){

inti=1;

//if(i==1)

//return0;

System.out.println("thepreviousstatementoftryblock");

i=i/0;

try{

System.out.println("tryblock");

returni;

}finally{

System.out.println("finallyblock");

}

}

}

清单1的执行结果如下:

thepreviousstatementoftryblock

Exceptioninthread"main"java.lang.ArithmeticException:

/byzero

at.bj.charlie.Test.test(Test.java:

15)

at.bj.charlie.Test.main(Test.java:

6)

另外,如果去掉上例中被注释的两条语句前的注释符,执行结果则是:

returnvalueoftest():

0

在以上两种情况下,finally语句块都没有执行,说明什么问题呢?

只有与finally相对应的try语句块得到执行的情况下,finally语句块才会执行。

以上两种情况,都是在try语句块之前返回(return)或者抛出异常,所以try对应的finally语句块没有执行。

那好,即使与finally相对应的try语句块得到执行的情况下,finally语句块一定会执行吗?

不好意思,这次可能又让大家失望了,答案仍然是否定的。

请看下面这个例子(清单2)。

清单2.

publicclassTest{

publicstaticvoidmain(String[]args){

System.out.println("returnvalueoftest():

"+test());

}

publicstaticinttest(){

inti=1;

try{

System.out.println("tryblock");

System.exit(0);

returni;

}finally{

System.out.println("finallyblock");

}

}

}

清单2的执行结果如下:

tryblock

finally语句块还是没有执行,为什么呢?

因为我们在try语句块中执行了System.exit(0)语句,终止了Java虚拟机的运行。

那有人说了,在一般的Java应用中基本上是不会调用这个System.exit(0)方法的。

OK!

没有问题,我们不调用System.exit(0)这个方法,那么finally语句块就一定会执行吗?

再一次让大家失望了,答案还是否定的。

当一个线程在执行try语句块或者catch语句块时被打断(interrupted)或者被终止(killed),与其相对应的finally语句块可能不会执行。

还有更极端的情况,就是在线程运行try语句块或者catch语句块时,突然死机或者断电,finally语句块肯定不会执行了。

可能有人认为死机、断电这些理由有些强词夺理,没有关系,我们只是为了说明这个问题。

回页首

finally语句剖析

说了这么多,还是让我们拿出些有说服力的证据吧!

还有什么证据比官方的文档更具说服力呢?

让我们来看看官方上的《TheJavaTutorials》中是怎样来描述finally语句块的吧!

以下位于****之间的容原封不动的摘自于《TheJavaTutorials》文档。

*******************************************************************************

ThefinallyBlock

Thefinallyblockalwaysexecuteswhenthetryblockexits.Thisensuresthatthefinallyblockisexecutedevenifanunexpectedexceptionoccurs.Butfinallyisusefulformorethanjustexceptionhandling—itallowstheprogrammertoavoidhavingcleanupcodeaccidentallybypassedbyareturn,continue,orbreak.Puttingcleanupcodeinafinallyblockisalwaysagoodpractice,evenwhennoexceptionsareanticipated.

Note:

IftheJVMexitswhilethetryorcatchcodeisbeingexecuted,thenthefinallyblockmaynotexecute.Likewise,ifthethreadexecutingthetryorcatchcodeisinterruptedorkilled,thefinallyblockmaynotexecuteeventhoughtheapplicationasawholecontinues.

*******************************************************************************

请仔细阅读并认真体会一下以上两段英文,当你真正的理解了这两段英文的确切含义,你就可以非常自信的来回答“finally语句块是否一定会执行?

”这样的问题。

看来,大多时候,并不是Java语言本身有多么高深,而是我们忽略了对基础知识的深入理解。

接下来,我们看一下finally语句块是怎样执行的。

在排除了以上finally语句块不执行的情况后,finally语句块就得保证要执行,既然finally语句块一定要执行,那么它和try语句块与catch语句块的执行顺序又是怎样的呢?

还有,如果try语句块中有return语句,那么finally语句块是在return之前执行,还是在return之后执行呢?

带着这样一些问题,我们还是以具体的案例来讲解。

关于try、catch、finally的执行顺序问题,我们还是来看看权威的论述吧!

以下****之间的容摘自Java语言规第四版(《TheJava™ProgrammingLanguage,FourthEdition》)中对于try,catch,和finally的描述。

*******************************************************************************

12.4.Try,catch,andfinally

YoucatchexceptionsbyenclosingcodeinTryblocks.ThebasicsyntaxforaTryblockis:

try{

statements

}catch(exception_type1identifier1){

statements

}catch(exception_type2identifier2){

statements

...

}finally{

statements

}

whereeitheratleastonecatchclause,orthefinallyclause,mustbepresent.Thebodyofthetrystatementisexecuteduntileitheranexceptionisthrownorthebodyfinishessuccessfully.Ifanexceptionisthrown,eachcatchclauseisexaminedinturn,fromfirsttolast,toseewhetherthetypeoftheexceptionobjectisassignabletothetypedeclaredinthecatch.Whenanassignablecatchclauseisfound,itsblockisexecutedwithitsidentifiersettoreferencetheexceptionobject.Noothercatchclausewillbeexecuted.Anynumberofcatchclauses,includingzero,canbeassociatedwithaparticularTRyaslongaseachclausecatchesadifferenttypeofexception.Ifnoappropriatecatchisfound,theexceptionpercolatesoutofthetrystatementintoanyoutertrythatmighthaveacatchclausetohandleit.

Ifafinallyclauseispresentwithatry,itscodeisexecutedafterallotherprocessinginthetryiscomplete.Thishappensnomatterhowcompletionwasachieved,whethernormally,throughanexception,orthroughacontrolflowstatementsuchasreturnorbreak.

*******************************************************************************

上面这段文字的大体意思是说,不管try语句块正常结束还是异常结束,finally语句块是保证要执行的。

如果try语句块正常结束,那么在try语句块中的语句都执行完之后,再执行finally语句块。

如果try中有控制转移语句(return、break、continue)呢?

那finally语句块是在控制转移语句之前执行,还是之后执行呢?

似乎从上面的描述中我们还看不出任何端倪,不要着急,后面的讲解中我们会分析这个问题。

如果try语句块异常结束,应该先去相应的catch块做异常处理,然后执行finally语句块。

同样的问题,如果catch语句块中包含控制转移语句呢?

finally语句块是在这些控制转移语句之前,还是之后执行呢?

我们也会在后续讨论中提到。

其实,关于try,catch,finally的执行流程远非这么简单,有兴趣的读者可以参考Java语言规第三版(《TheJava™LanguageSpecification,ThirdEdition》)中对于Executionoftry-catch-finally的描述,非常复杂的一个流程。

限于篇幅的原因,本文不做摘录,请感兴趣的读者自行阅读。

回页首

finally语句示例说明

下面,我们先来看一个简单的例子(清单3)。

清单3.

publicclassTest{

publicstaticvoidmain(String[]args){

try{

System.out.println("tryblock");

return;

}finally{

System.out.println("finallyblock");

}

}

}

清单3的执行结果为:

tryblock

finallyblock

清单3说明finally语句块在try语句块中的return语句之前执行。

我们再来看另一个例子(清单4)。

清单4.

publicclassTest{

publicstaticvoidmain(String[]args){

System.out.println("returevalueoftest():

"+test());

}

publicstaticinttest(){

inti=1;

try{

System.out.println("tryblock");

i=1/0;

return1;

}catch(Exceptione){

System.out.println("exceptionblock");

return2;

}finally{

System.out.println("finallyblock");

}

}

}

清单4的执行结果为:

tryblock

exceptionblock

finallyblock

returevalueoftest():

2

清单4说明了finally语句块在catch语句块中的return语句之前执行。

从上面的清单3和清单4,我们可以看出,其实finally语句块是在try或者catch中的return语句之前执行的。

更加一般的说法是,finally语句块应该是在控制转移语句之前执行,控制转移语句除了return外,还有break和continue。

另外,throw语句也属于控制转移语句。

虽然return、throw、break和continue都是控制转移语句,但是它们之间是有区别的。

其中return和throw把程序控制权转交给它们的调用者(invoker),而break和continue的控制权是在当前方法转移。

请大家先记住它们的区别,在后续的分析中我们还会谈到。

还是得来点有说服力的证据,下面这段摘自Java语言规第四版(《TheJava™ProgrammingLanguage,FourthEdition》),请读者自己体会一下其含义。

*******************************************************************************

Afinallyclausecanalsobeusedtocleanupforbreak,continue,andreturn,whichisonereasonyouwillsometimesseeatryclausewithnocatchclauses.Whenanycontroltransferstatementisexecuted,allrelevantfinallyclausesareexecuted.Thereisnowaytoleaveatryblockwithoutexecutingitsfinallyclause.

*******************************************************************************

好了,看到这里,是不是有人认为自己已经掌握了finally的用法了?

先别忙着下结论,我们再来看两个例子–清单5和清单6。

清单5.

publicclassTest{

publicstaticvoidmain(String[]args){

System.out.println("returnvalueofgetValue():

"+getValue());

}

publicstaticintgetValue(){

try{

return0;

}finally{

return1;

}

}

}

清单5的执行结果:

returnvalueofgetValue():

1

清单6.

publicclassTest{

publicstaticvoidmain(String[]args){

System.out.println("returnvalueofgetValue():

"+getValue());

}

publicstaticintgetValue(){

inti=1;

try{

returni;

}finally{

i++;

}

}

}

清单6的执行结果:

returnvalueofgetValue():

1

利用我们上面分析得出的结论:

finally语句块是在try或者catch中的return语句之前执行的。

由此,可以轻松的理解清单5的执行结果是1。

因为finally中的return1;语句要在try中的return0;语句之前执行,那么finally中的return1;语句执行后,把程序的控制权转交给了它的调用者main()函数,并且返回值为1。

那为什么清单6的返回值不是2,而是1呢?

按照清单5的分析逻辑,finally中的i++;语句应该在try中的returni;之前执行啊?

i的初始值为1,那么执行i++;之后为2,再执行returni;那不就应该是2吗?

怎么变成1了呢?

关于Java虚拟机是如何编译finally语句块的问题,有兴趣的读者可以参考《TheJavaTMVirtualMachineSpecification,SecondEdition》中7.13节Compilingfinally。

那里详细介绍了Java虚拟机是如何编译finally语句块。

实际上,Java虚拟机会把finally语句块作为subroutine(对于这个subroutine不知该如何翻译为好,干脆就不翻译了,免得产生歧义和误解。

)直接插入到try语句块或者catch语句块的控制转移语句之前。

但是,还有另外一个不可忽视的因素,那就是在执行subroutine(也就是finally语句块)之前,try或者catch语句块会保留其返回值到本地变量表(LocalVariableTable)中。

待subroutine执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过return或者throw语句将其返回给该方法的调用者(invoker)。

请注意,前文中我们曾经提到过return、throw和break、continue的区别,对于这条规则(保留返回值),只适用于return和throw语句,不适用于break和continue语句,因为它们根本就没有返回值。

是不是不太好理解,那我们就用具体的例子来做形象的说明吧!

为了能够解释清单6的执行结果,我们来分析一下清单6的字节码(byte-code):

Compiledfrom"Test.java"

publicclassTestextendsjava.lang.Object{

publicTest();

Code:

0:

aload_0

1:

invokespecial#1;//Methodjava/lang/Object."":

()V

4:

return

LineNumberTable:

line1:

0

publicstaticvoidmain(java.lang.String[]);

Code:

0:

getstatic#2;//Fieldjava/lang/System.out:

Ljava/io/PrintStream;

3:

new#3;//classjava/lang/StringBuilder

6:

dup

7:

invokespecial#4;//Methodjava/lang/StringBuilder."":

()V

10:

ldc#5;//StringreturnvalueofgetValue():

12:

invokevirtual

#6;//Methodjava/lang/StringBuilder.append:

Ljava/lang/String;)Ljava/lang/StringBuilder;

15:

invokestatic#7;//Methodg

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 人文社科 > 法律资料

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2