从字节码角度看try catch finally处理过程

简介: 本文通过分析一段示例代码的字节码指令,讨论异常流程的处理。

本文通过分析一段示例代码的字节码指令,讨论异常流程的处理。

 

一  示例代码

代码如下,在讨论异常处理流程前,不妨先思考一下,在抛出异常和不抛出异常时,返回的结果是多少?是3,是4,还是5?为什么?


    public int test() {
        int x ;
        // int y = 0 ;
        try {
            x = 3;
            // int z = x/y ;
            return x;
        } catch (Exception e) {
            x = 4;
            return x;
        } finally {
            x = 5;
        }
    }
 

二  字节码指令介绍

简单介绍一下示例中使用到的指令,更多指令见虚拟机指令集。

指令

意义

iload_n(0~3)

将局部变量表中第n个槽的(int)变量推送到操作数栈。

n大于3时,指令改为iload n,例如iload 4表示将第4个槽的变量存到操作数栈中

istore_n(0~3)

将操作数栈顶的值弹出存到局部变量表的第n个槽中。

n大于3时,指令改为istore n,例如istore 5表示将操作数栈中的值弹出并存储到局部变量表的第5个槽中

astore_n(0~3)

将栈顶引用类型的值存到局部变量表中的第n个槽中

n大于3时,指令改为 astore n

iconst_n (−1~5)

将常量n入栈

n<-1或者n>5时,指令改为bipush n

bipush n

将一个常量入栈

 

三  编译后的字节码指令

上面的示例代码,不论是否抛出异常,返回结果均不会是5,接下来我们以不抛出异常时编译出来的的字节码指令讨论一下这个问题。

1   字节码分析

以下为编译成字节码以后的代码,以及每一步的意义。

  public int test();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=5, args_size=1
         0: iconst_3            // 对应try块中的x=3,并将3入栈(操作数栈)
         1: istore_1            // 将栈顶元素(3)弹出并存储到局部变量表第1个slot中
         2: iload_1             // 加载局部变量表中第1个slot的值(3)到栈栈顶
         3: istore        4    // 将栈顶的元素(3)存储到局部变量表第4个slot中
         5: iconst_5            // 对应于finally块中x=5,并将5入栈
         6: istore_1            // 将栈顶元素(5)弹出并存储到局部变量表第1个slot中
         7: iload         4    // 加载局部变量表第4个slot的值(3)到栈中
         9: ireturn             // 弹出栈顶元素并返回
        10: astore_2            // 将栈顶引用元素(exception弹出并存储在局部变量表第2个slot中
        11: iconst_4            // 对应catch块中的x=4,并将4入栈
        12: istore_1            // 将栈顶元素(4)弹出并存储在局部变量表第1个slot中
        13: iload_1             // 将局部变量表第1个slot元素(4)加载到栈顶
        14: istore        4    // 将栈顶元素(4)弹出并存储在局部变量表第4个slot中
        16: iconst_5            // 对应于finally块中x=5,并将5入栈
        17: istore_1            // 将栈顶元素(5)弹出并存储到局部变量表第1个slot中
        18: iload         4    // 加载局部变量表第4个slot的值(4)到栈中
        20: ireturn             // 弹出栈顶元素并返回
        21: astore_3            // 如果出现不属于Exception或其子类会执行此指令,将栈顶元素(Exception)弹出并存储到局部变量表第3个slot中
        22: iconst_5            // 对应于finally块中x=5,并将5入栈
        23: istore_1            // 将栈顶元素(5)弹出并存储到局部变量表第1个slot中
        24: aload_3             // 将局部变量表中第3个slot的值(Exception)加到栈顶
        25: athrow              // 弹出栈顶元素并抛出异常
      Exception table:
         from   to  target type
             0     5   10   Classjava/lang/Exception    //如果try中出现Exception异常(或其子类),那么跳转转到catch语句块处理
             0     5   21   any                          //如果try语句快出现了不属于Exception的异常,那么进入finally语句块处理
            10    16   21   any                          //如果catch出现任何异常,那么进入finally语句块处理


 

2   输出结果分析

通过字节码指令分析,我们可以看到ireturn指令返回的都是从局部变量表第4个slot中取出的值,而此值在执行finally之前已经存储好了,并且在执行finally代码块之间没有做任何修改,所以示例代码不会返回5,如果抛出异常返回4,不抛出异常返回3;




 

相关文章
|
Java UED
【JavaSE专栏68】异常的捕获和处理,try-catch-finally高效解决异常处理问题
【JavaSE专栏68】异常的捕获和处理,try-catch-finally高效解决异常处理问题
111 0
|
1月前
|
Java
如何使用 try-catch 块来捕获静态变量初始化中的异常
在Java中,可以通过在静态初始化块或静态变量初始化时使用try-catch语句来捕获可能出现的异常,确保程序的健壯性。具体做法是在静态初始化代码中加入try-catch结构,对可能抛出的异常进行处理。
68 16
|
1月前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
50 2
|
4月前
|
网络协议 Java 数据库连接
13 Java异常(异常过程解析、throw、throws、try-catch关键字)
13 Java异常(异常过程解析、throw、throws、try-catch关键字)
102 2
|
6月前
|
Java 程序员 数据库连接
Java中的异常处理:理解try-catch块的工作原理
本文深入探讨了Java编程语言中异常处理的核心机制——try-catch块。我们将通过具体示例,详细解释异常的产生、捕获和处理过程,以及如何有效地利用这一机制来提高代码的健壮性和可维护性。
Catch块异常多类抓取
Catch块异常多类抓取
60 0
|
JSON 安全 前端开发
替代try catch处理异常的优雅方式
替代try catch处理异常的优雅方式
|
Java
Java异常——处理机制Try-catch-finally
Java异常——处理机制Try-catch-finally
193 0
|
缓存 前端开发 Java
如何优雅的实现 try catch 异常块?
如何优雅的实现 try catch 异常块?
303 0
如何优雅的实现 try catch 异常块?
|
编译器
有return的情况下try catch finally的执行顺序(最有说服力的总结)
有return的情况下try catch finally的执行顺序(最有说服力的总结)
170 0