zoukankan      html  css  js  c++  java
  • 一道面试题引发的思考

    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不管对变量如何操作,只要在返回之前加载新变量即可。

  • 相关阅读:
    性能相差7千倍的ToString方法
    重构打造爱因斯坦谜题最快算法
    Windows Phone 7将胜出的五条论据
    让火狐狸遨游起来
    What's your understanding about RIA?
    [English Practise]Action when meeting a problem at work
    linux socket编程
    nginx服务器的配置
    要搬到csdn了
    搭建一个全栈式的HTML5移动应用框架
  • 原文地址:https://www.cnblogs.com/macula7/p/1960417.html
Copyright © 2011-2022 走看看