• 字节码分析finally块对return返回值的影响


    直接进入主题。看如下代码:

    public int test(){
        int i=0;
        try {
            i=1;
            return i;
        } catch (Exception e) {
            i=2;
            return i;
        }finally{
            i=3;
        }
    }

    相信有点经验的程序员一眼就能说出返回的结果为1,但是您真的知道返回的结果为什么为1吗?下面我们通过分析下当前方法的字节码,来说明为什么。

    查看字节码命令:javap -verbose class文件

    知识点简单概要:
    看如下字节码需要简单了解下栈的结构。
    栈包括:局部变量表、操作数栈、动态连接、方法出口等。
    下面字节码主要是对操作栈和局变量表的操作。

    test方法的字节码如下:

     stack=1, locals=5, args_size=1
        0: iconst_0                 将常量0压入到操作栈顶
        1: istore_1                  将栈顶元素0存储到局部变量表中的第二个slot中  (slot2=0)  i=0
        2: iconst_1                 将常量1压入到操作栈顶
        3: istore_1                  将栈顶元素1存储到局部变量表中的第二个slot中  (slot2=1)  i=1
        4: iload_1                   将局部变量中第二个变量 (i=1) 压入到操作栈顶
        5: istore        4            将栈顶元素1存储到局部变量表中的第四个slot中  (slot4=1) 
        7: iconst_3                 将常量3压入到操作栈顶
        8: istore_1                  将栈顶元素3存储到局部变量表中的第二个slot中  (slot2=1)  i=3
        9: iload         4            将局部变量中第四个变量 (slot4=1) 压入到操作栈顶
       11: ireturn                   返回操作栈顶值,这时操作栈中栈顶值为1。
       12: astore_2               将栈顶元素(e)存储到局部变量表的第三个slot中
       13: iconst_2                将常量2压入到操作栈顶
       14: istore_1                 将栈顶元素2存储到局部变量表中的第二个slot中  (slot2=2)  i=2
       15: iload_1                  将局部变量中第二个变量 (i=2) 压入到操作栈顶
       16: istore        4           将栈顶元素2存储到局部变量表中的第四个slot中  (slot4=2) 
       18: iconst_3                将常量3压入到操作栈顶
       19: istore_1                 将栈顶元素3存储到局部变量表中的第二个slot中  (slot2=1)  i=3
       20: iload         4           将局部变量中第四个变量 (slot4=2) 压入到操作栈顶
       22: ireturn                   返回操作栈顶值,这时操作栈中栈顶值为2。
       23: astore_3               将栈顶元素(其它异常,Exception之外的)存储到局部变量表的第四个slot中
       24: iconst_3                将常量3压入到操作栈顶
       25: istore_1                将栈顶元素3存储到局部变量表中的第二个slot中  (slot2=3)  i=3
       26: aload_3                将局部变量中第四个变量 (其它异常) 压入到操作栈顶
       27: athrow                  抛出栈顶元素(异常信息) 无返回值

    stack=1, locals=5, args_size=1

    • stack=1:操作栈的深度
    • locals=5:局部变量表中5个slot(槽位),每个slot存储能一个变量(long、double 需要两个slot存储)
      1. this变量
      2. i 变量
      3. e 变量(Exception)
      4. Exception之外异常的变量
      5. 临时存储变量(返回值从临时存储中返回的)
    • args_size=1: 方法的参数个数(该方法无参数,为什么这里args_size为1呢?因为这个是实例方法,不是静态方法,他默认会传过来当前实例的引用,也就是this变量)

    字节码执行路径

    通过字节码我们发现在编译成class文件的时候,已经把三种执行路径都写到class文件中了。

    1. 第一种路径
      第【1-11】行,程序正常执行顺序(无异常)
    2. 第二种路径
      第【12-22】行,程序报Exception的异常
    3. 第三种路径
      第【23-27】行,程序报Exception之外的异常

    字节码大白话解释说明

    第[0-1]行,代码:int i=0;
    第[2-3]行,try块中代码:i=1;
    第[4-5]行,遇到return时,把 i 的值临时存储起来,然后执行finally中的代码。
    第[7-8] 行,finally块代码:i=3
    第[9-11] 行,执行return语句,把临时存储的 i 值返回。(执行finally代码对返回值无影响)
    第[12]行,catch块代码: (Exception e)
    第[13-14]行,catch块代码:  i=2
    第[15-16]行,遇到catch块中的return时,把 i 的值临时存储起来,然后执行finally中的代码。
    第[18-19]行,finally块代码:i=3
    第[20-22]行,执行catch块中return语句,把临时存储的 i 值返回。(执行finally代码对返回值无影响)
    第[23]行,局部变量表中存储Exception之外的异常
    第[24-25]行,finally块代码:i=3
    第[26-27]行,将Exception之外的异常压入栈顶,并抛出(无返回值)

    结论

    通过字节码,我们发现,在try语句的return块中,return 返回的变量并不是直接返回 i 值,而是在执行finally块之前把i值存储在临时区域,当执行return时直接返回的临时区域中的值,即使在finally语句中把变量 i 的值修改了,也不会影响返回的值。

  • 相关阅读:
    BAT 批处理脚本教程
    javascript定时器
    使用命令行打开文件夹并显示
    用cmd加密文件夹
    烟波钓叟歌概述讲解
    奇门遁甲的起源
    八卦基本知识
    word2vec和word embedding有什么区别?
    Privoxy shadowscocks代理
    Elasticsearch源码分析—线程池(十一) ——就是从队列里处理请求
  • 原文地址:https://www.cnblogs.com/printN/p/6880034.html
Copyright © 2020-2023  润新知