一道面试题引发的思考

try里面有return,finally 语句还执行么?执行的话是在return之前执行,还是之后执行。

答案:执行,return之前。

实例:

class test{
public int t() {
   int i = 2;
   try {
    return i;
   } finally {
    i = 9;
    System.out.println(i);
   }
}

public static void main(String[] arsg) {
   System.out.println(new test().t());
}
}

这个程序会输出什么:

9

2

为什么?

反编译一下:

C:\study>javap -c -v test
Compiled from "test.java"
class test extends java.lang.Object
SourceFile: "test.java"
minor version: 0
major version: 50
Constant pool:
const #1 = Method       #7.#21; // java/lang/Object."<init>":()V
const #2 = Field        #22.#23;        // java/lang/System.out:Ljava/io/PrintS
tream;
const #3 = Method       #24.#25;        // java/io/PrintStream.println:(I)V
const #4 = class        #26;    // test
const #5 = Method       #4.#21; // test."<init>":()V
const #6 = Method       #4.#27; // test.t:()I
const #7 = class        #28;    // java/lang/Object
const #8 = Asciz        <init>;
const #9 = Asciz        ()V;
const #10 = Asciz       Code;
const #11 = Asciz       LineNumberTable;
const #12 = Asciz       t;
const #13 = Asciz       ()I;
const #14 = Asciz       StackMapTable;
const #15 = class       #26;    // test
const #16 = class       #29;    // java/lang/Throwable
const #17 = Asciz       main;
const #18 = Asciz       ([Ljava/lang/String;)V;
const #19 = Asciz       SourceFile;
const #20 = Asciz       test.java;
const #21 = NameAndType #8:#9;// "<init>":()V
const #22 = class       #30;    // java/lang/System
const #23 = NameAndType #31:#32;// out:Ljava/io/PrintStream;
const #24 = class       #33;    // java/io/PrintStream
const #25 = NameAndType #34:#35;// println:(I)V
const #26 = Asciz       test;
const #27 = NameAndType #12:#13;// t:()I
const #28 = Asciz       java/lang/Object;
const #29 = Asciz       java/lang/Throwable;
const #30 = Asciz       java/lang/System;
const #31 = Asciz       out;
const #32 = Asciz       Ljava/io/PrintStream;;
const #33 = Asciz       java/io/PrintStream;
const #34 = Asciz       println;
const #35 = Asciz       (I)V;

{
test();
Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return
LineNumberTable:
   line 1: 0


public int t();
Code:
   Stack=2, Locals=4, Args_size=1
   0:   iconst_2     //push 2 onto the stack,将“2”加载到堆栈上
   1:   istore_1     //Pops an int off the stack and stores it in local variable <n>, store integer in local variable 1,取得堆栈值,存放到变量1
   2:   iload_1      //push integer in local variable 1 onto the stack,将变量1的值放入堆栈,此时值为“2”
   3:   istore_2     //store integer in local variable 2,将堆栈的值存放在变量2中,变量2值为2
   4:   bipush 9     //将9放入堆栈
   6:   istore_1     //将堆栈的值存入变量1,此时变量1的值为9
   7:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;这里应该是加载System和PrintStream类
   10: iload_1      //将变量1的值放入堆栈,堆栈值为9
   11: invokevirtual   #3; //Method java/io/PrintStream.println:(I)V打印
   14: iload_2      //将变量2的值放入堆栈,此时为堆栈为“2”
   15: ireturn      //返回
   16: astore_3     //Pops objectref (a reference to an object or array) off the stack and stores it in local variable <n>,
   17: bipush 9     //
   19: istore_1     //
   20: getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   23: iload_1
   24: invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   27: aload_3      //push object in local variable 3
   28: athrow
Exception table:

   from   to target type
     2     4    16   any
    16    17    16   any
LineNumberTable:
   line 3: 0
   line 5: 2
   line 7: 4
   line 8: 7
   line 7: 16
   line 8: 20

StackMapTable: number_of_entries = 1
   frame_type = 255 /* full_frame */
     offset_delta = 16
     locals = [ class test, int ]
     stack = [ class java/lang/Throwable ]


public static void main(java.lang.String[]);
Code:
   Stack=3, Locals=1, Args_size=1
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   new     #4; //class test
   6:   dup
   7:   invokespecial   #5; //Method "<init>":()V
   10: invokevirtual   #6; //Method t:()I
   13: invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   16: return
LineNumberTable:
   line 13: 0
   line 14: 16


}

注意加黑的t()方法,里面注释是自己加的。参考自http://www.daimi.au.dk/dOvs/jvmspec/ref-Java.html,不一定完全正确(抽空看看jvm的书)

看test方法  

0:   iconst_2     //push 2 onto the stack,将“2”加载到堆栈上
   1:   istore_1     //Pops an int off the stack and stores it in local variable <n>, store integer in local variable 1,取得堆栈值,存放到变量1
   2:   iload_1      //push integer in local variable 1 onto the stack,将变量1的值放入堆栈,此时值为“2”
   3:   istore_2     //store integer in local variable 2,将堆栈的值存放在变量2中,变量2值为2
   4:   bipush 9     //将9放入堆栈
   6:   istore_1     //将堆栈的值存入变量1,此时变量1的值为9
   7:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;这里应该是加载System和PrintStream类
   10: iload_1      //将变量1的值放入堆栈,堆栈值为9
   11: invokevirtual   #3; //Method java/io/PrintStream.println:(I)V打印
   14: iload_2      //将变量2的值放入堆栈,此时为堆栈为“2”
   15: ireturn      //返回
   16: astore_3     //Pops objectref (a reference to an object or array) off the stack and stores it in local variable <n>,
   17: bipush 9     //
   19: istore_1     //
   20: getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   23: iload_1
   24: invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   27: aload_3      //push object in local variable 3
   28: athrow

带着注释的话应该能看懂,大家可以看到finally语句在return之前,但是怎么保证输出的是2而不是finally里改成的9呢。

istore_2            //finally之前将内容存入变量2

finally内容

14: iload_2      //将变量2的值放入堆栈,此时为堆栈为“2” //返回之前加载变量2
   15: ireturn     //返回

我们验证一下自己的观点:

class testTwo{
public int t() {
   int i = 2;
   try {
    return i;
   } finally {
   
   }
}

public static void main(String[] arsg) {
   System.out.println(new testTwo().t());
}
}

在这段代码里,finnaly语句什么也没干:

对应的结果是:


public int t();
Code:
   Stack=1, Locals=4, Args_size=1
   0:   iconst_2
   1:   istore_1
   2:   iload_1
   3:   istore_2    //finally应该在两者之间,但是由于什么也没干,所以为空
   4:   iload_2

   5:   ireturn
   6:   astore_3
   7:   aload_3
   8:   athrow
Exception table:
   from   to target type
     2     4     6   any
     6     7     6   any
LineNumberTable:
   line 3: 0
   line 5: 2
   line 6: 6

在整个3看看:

class testThree{
public int t() {
   int i = 2;
   try {
    return i;
   } finally {
    int j=5;
    j=3;
   }
}

public static void main(String[] arsg) {
   System.out.println(new testThree().t());
}
}
对应的结果为:

public int t();
Code:
   Stack=1, Locals=6, Args_size=1
   0:   iconst_2
   1:   istore_1
   2:   iload_1
3:   istore_2
   4:   iconst_5
   5:   istore_3
   6:   iconst_3
   7:   istore_3
   8:   iload_2
   9:   ireturn   //可以看到返回之前,总是要加载变量2

   10: astore 4
   12: iconst_5
   13: istore 5
   15: iconst_3
   16: istore 5
   18: aload   4
   20: athrow
Exception table:

然后我们把程序改为最简单的形式:

class testTwo{
public int t() {
   int i = 2;
   return i;
  
}

public static void main(String[] arsg) {
   System.out.println(new testTwo().t());
}
}
对应的结果就是:

public int t();
Code:
   Stack=1, Locals=2, Args_size=1
   0:   iconst_2
   1:   istore_1
   2:   iload_1
   3:   ireturn

LineNumberTable:
   line 3: 0
   line 4: 2

可以看到这里没有了变量2,因为你已经没有了finally

最后总结:

finally的语句应该是在return语句之前执行,但是为了保证值的不便,将值存放到一个新变量中,finally不管对变量如何操作,只要在返回之前加载新变量即可。

转载于:https://www.cnblogs.com/macula7/archive/2010/07/01/1960417.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值