zoukankan      html  css  js  c++  java
  • 一个问题:关于finally中return吞掉catch块中抛出的异常

    今天遇到一个感觉很神奇的问题,记录一下问题以及自己分析问题的思路。

    预警:不知道怎么看java字节码的朋友可能需要先看一下如何阅读java字节码才能看懂后面的解释。

    我有一段程序:

    public class Test {
        public static void main(String[] args) {
            try {
                int a = 1 / 0;
            } catch (Exception e) {
                throw e;
            }
        }
    }

    这个程序的运行结果相信大家都能猜到:

    在main方法里捕获异常没有处理直接往上层抛,最后异常打印到了控制台。

    现在我给这段代码加一个finally块,里面写个return,大家猜测一下运行结果:

    public class Test {
        public static void main(String[] args) {
            try {
                int a = 1 / 0;
            } catch (Exception e) {
                throw e;
            } finally {
                return;
            }
    
        }
    }

    如果你觉得运行结果会是程序正常运行没有报任何错误,那么你可以不用再继续读了,因为运行结果确实如此。

    如果你像笔者一样觉得运行结果应该和之前一样,程序依然会抛出异常,那么请继续往下看。

    笔者之前的想法是,这段程序运行的时候,会先执行try中的语句,然后发生了异常进入catch块,在catch块中抛出异常,此时程序已经结束,finally中的return其实没有什么意义。

    但是根据运行结果来看显然不是这样的,于是笔者将前后两段程序都用javap -verbose命令反汇编,结果如下(去掉了常量池等无关信息):

    不带finally的程序反汇编结果:

    public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
    stack=2, locals=2, args_size=1
    0: iconst_1
    1: iconst_0
    2: idiv
    3: istore_1
    4: goto 10
    7: astore_1
    8: aload_1
    9: athrow
    10: return


    Exception table:
    from to target type
    0 4 7 Class java/lang/Exception

    我们来分析一下这段代码的含义:

    第0~4行是程序执行的正常逻辑:

    0: iconst_1  //将int 1推至栈顶
    1: iconst_0  //将int 0推至栈顶
    2: idiv  //将栈顶两int型数值相除(即1/0)并将结果压入栈顶
    3: istore_1  //将栈顶的int值存入第二个本地变量(即赋值给a)
    4: goto 10  //跳转到第10行return

    然后异常表定义了执行0~4行时,如果发生异常就跳转到第7行,实际执行时会在第2行发生异常,因此我们看一下发生异常会走的逻辑:

    0: iconst_1  //将int 1推至栈顶
    1: iconst_0  //将int 0推至栈顶
    2: idiv  //预计将栈顶两int型数值相除(即1/0)并将结果压入栈顶,此时发生异常,实际将异常的引用放到栈顶,根据异常表跳到第7行

    7: astore_1  //将栈顶的异常引用存入第二个本地变量
    8: aload_1  //将异常放置到栈顶
    9: athrow  //抛出栈顶异常
    10: return

    带finally块的反汇编结果:

    
    

    public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
    stack=2, locals=2, args_size=1
    0: iconst_1
    1: iconst_0
    2: idiv
    3: istore_1
    4: goto 11
    7: astore_1
    8: aload_1
    9: athrow
    10: pop
    11: return


    Exception table:
    from to target type
    0 4 7 Class java/lang/Exception
    0 10 10 any

     

    仔细观察,有finally块的代码的汇编代码和无finally块的汇编代码几乎完全一致,只是多了一个pop,即我标黄的部分。

    从pop的位置来看,是已经执行了athrow抛出了栈顶的异常之后执行的,那么这个pop弹出的是什么东西呢?为什么多了这个pop异常就不会抛出了呢?

    我还要再查一下资料(逃~)

    附上athrow命令的解释:

    Description

    Removes objectref (a reference to an object) from the operand stack, and 'throws' the exception represented by that object. objectref is an instance of Throwable or one of its subclasses.

    To throw an exception, the system searches for a handler for objectref's class in the exception table of the currently active method.

    If no handler is found, the current method's frame is discarded, its invoker's frame is reinstated, and the exception is immediately rethrown. This process is repeated until a handler is found or until there are no more procedures on the callstack (at which point, the current thread dies, typically printing out an error message).

    If a handler is found, the operand stack of the active method is cleared, objectref is pushed on the operand stack of the current method, and execution continues at the first instruction of the handler.

    See Chapter 10 for a full description of exceptions in the JVM.

  • 相关阅读:
    Safe Path(bfs+一维数组存图)
    一维树状数组入门
    Ancient Go(简单DFS)
    Sudoku(简单DFS)
    Strategic game(树形DP入门)
    多线程源码分析
    Navicat premium12的破解方法,无需注册码
    MySQL和Oracle的区别
    java Web项目中什么场景中会用到java多线程?
    TCP协议三次握手、四次断开 过程分析
  • 原文地址:https://www.cnblogs.com/sheeva/p/6282067.html
Copyright © 2011-2022 走看看