前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >finally与return之间的关系

finally与return之间的关系

作者头像
zhangheng
发布于 2020-04-28 09:54:18
发布于 2020-04-28 09:54:18
95600
代码可运行
举报
运行总次数:0
代码可运行

定论

问:finally语句一定会执行吗? 答:

  1. 如果没有执行相应的try语句则不会执行。
  2. 在try语句中如果调用System.exit(0)方法则不会执行。

问:finally会在什么时候执行? 答:如果在try/catch语句中调用转移指令例如:return,break,continue,throw等。则会在转移指令前执行。

总结

finally与return之间的关系

如果在finally中含有return语句,那么try/catch语句的return还有作用吗?

先看一段代码:

代码语言:javascript
代码运行次数:0
运行
复制
/**
 * Created by gavin on 15-9-2.
 */
public class FinallyTest {
    public static void main(String[] args){
        System.out.println(test1());    //3
        System.out.println(test2());    //3
        System.out.println(test3());    //2
        System.out.println(test4());    //2
    }
    public static int test1()
    {
        int i = 1;
        try {
            i = 2;
            return i;
        }finally {
            i++;
            return i;
        }
    }
    public static int test2()
    {
        int i = 1;
        try {
            i = 2;
            return i;
        }finally {
            i = 3;
            return i;
        }
    }
    public static int test3()
    {
        int i = 1;
        try {
            i = 2;
            return i;
        }finally {
            i++;
        }
    }
    public static int test4()
    {
        int i = 1;
        try {
            i = 2;
            return i;
        }finally {
            i = 3;
        }
    }
}

如果你对java内存布局不是很清楚,请看这篇文章:java虚拟机类加载机制和字节码执行引擎

重点关注运行时栈帧结构(局部变量表槽操作数栈)。

上边的代码非常简单,来看一下字节码指令吧

代码语言:javascript
代码运行次数:0
运行
复制
public static int test1();
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=3, args_size=0
         0: iconst_1        //定义一个常量1入栈到操作数栈            
         //栈1  0:  1:
         1: istore_0        //出栈,存储到局部便量表槽0         
         //栈   0:1 1:
         2: iconst_2        //定义一个常量2入栈到操作数栈            
         //栈2  0:1 1:
         3: istore_0        //出栈,存储到局部变量表槽0         
         //栈   0:2 1:
         4: iload_0        //从局部便量表槽0入栈到操作数栈   
         //栈2  0:2 1:
         5: istore_1        //出栈,存储到局部变量表槽1         
         //栈   0:2 1:2
         6: iinc          0, 1 //局部变量表槽0变量加1               
         //栈   0:3 1:2
         9: iload_0        //从局部变量表槽0入栈到操作数栈   
         //栈3  0:3 1:2
        10: ireturn         //结束,返回                                 
        //栈3   0:3 1:2
        11: astore_2      
        12: iinc          0, 1
        15: iload_0       
        16: ireturn       
   
  public static int test2();
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=3, args_size=0
         0: iconst_1        //定义一个常量1入栈到操作数栈    
         //栈1  0:  1:
         1: istore_0        //出栈,存储到局部便量表槽0 
         //栈   0:1 1:
         2: iconst_2        //定义一个常量2入栈到操作数栈    
         //栈2  0:1 1:
         3: istore_0        //出栈,存储到局部变量表槽0 
         //栈   0:2 1:
         4: iload_0        //从局部变量表槽0入栈                
         //栈2  0:2 1:
         5: istore_1        //出栈,存储到局部变量表槽1 
         //栈   0:2 1:2
         6: iconst_3        //定义一个常量3入栈                 
         //栈3  0:2 1:2
         7: istore_0        //出栈,存储到局部便量表槽0 
         //栈   0:3 1:2
         8: iload_0        //从局部变量表槽0入栈                
         //栈3  0:3 1:2
         9: ireturn        //结束,返回                         
         //栈3  0:3 1:2
        10: astore_2         
        11: iconst_3      
        12: istore_0      
        13: iload_0       
        14: ireturn       
    
  public static int test3();
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=3, args_size=0
         0: iconst_1        //定义一个常量1入栈到操作数栈    
         //栈1  0:  1:
         1: istore_0        //出栈,存储到局部便量表槽0 
         //栈   0:1 1:
         2: iconst_2        //定义一个常量2入栈到操作数栈    
         //栈2  0:1 1:
         3: istore_0        //出栈,存储到局部变量表槽0 
         //栈   0:2 1:
         4: iload_0        //从局部变量表槽0入栈                
         //栈2  0:2 1:
         5: istore_1        //出栈,存储到局部变量表槽1 
         //栈   0:2 1:2
         6: iinc          0, 1 //局部变量表槽0变量加一       
         //栈   0:3 1:2
         9: iload_1        //从局部变量表槽1入栈                
         //栈2  0:3 1:2
        10: ireturn         //结束,返回                         
        //栈2   0:3 1:2
        11: astore_2      
        12: iinc          0, 1
        15: aload_2       
        16: athrow        
   
  public static int test4();
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=3, args_size=0
         0: iconst_1        //定义一个常量1入栈到操作数栈    
         //栈1  0:  1:
         1: istore_0        //出栈,存储到局部便量表槽0 
         //栈   0:1 1:
         2: iconst_2        //定义一个常量2入栈到操作数栈    
         //栈2  0:1 1:
         3: istore_0        //出栈,存储到局部变量表槽0 
         //栈   0:2 1:
         4: iload_0        //从局部变量表槽0入栈                
         //栈2  0:2 1:
         5: istore_1        //出栈,存储到局部变量表槽1 
         //栈   0:2 1:2
         6: iconst_3        //定义一个常量3入栈到操作数栈    
         //栈3  0:2 1:2
         7: istore_0        //出栈,存储到局部变量表槽0 
         //栈   0:3 1:2
         8: iload_1        //从局部变量表槽1入栈                
         //栈2  0:3 1:2
         9: ireturn        //结束,返回                         
         //栈2  0:3 1:2
        10: astore_2      
        11: iconst_3      
        12: istore_0      
        13: aload_2       
        14: athrow

我们看到:

在finally中没有return时,栈中最后存储的数据是try/catch中操作后数据。即finally操作后的数据存储到其他槽中,而后再加载try/catch操作后的数据。 而在finally中含有return时,栈中最后存储的数据是finally中操作后的数据。即finally操作后的数据存储到其他槽中,而后加载的是其他槽(finally)中的数据。

也就是说:如果finally中不含有return语句,finally对try/catch操作的八大基础类型不会再加载到操作数栈中。

如果返回值是对象引用,finally中的return还有待考据。

参考:关于 Java 中 finally 语句块的深度辨析

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015-09-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
深入方法区
方法区(Method Area)属于jvm运行时数据区的一块,也是跟堆一样被所有线程共享,并且方法区在物理层面是属于堆中的一块。方法区在JVM启动的时候被创建。
逍遥壮士
2021/04/13
4480
深入方法区
通过字节码理解try-catch-finally
结合这个异常表和Code中的注释,可以发现,如果try语句中发生了Exception及其子类异常,那么执行的字节码为第8-16行,最终返回值为2。其他异常的话,则跳到第17行处理,执行第17-23行,最终将异常抛出,方法值没有返回。
Java架构师必看
2021/11/29
3670
一个try-catch问出这么多花样【面试题】
上面代码的字节码部分如下图所示(其中红色的字为解析,下面会对详细内容进行解释)
CBeann
2023/12/25
1620
一个try-catch问出这么多花样【面试题】
1、引言
栈帧对应一个线程的一个方法的内容,用于方法的执行,包括方法执行过程中的变量的临时状态。同时栈帧也执行动态链接,方法的返回值以及分发异常。栈帧被包含在JVM栈中。每一个栈帧包括:
文彬
2022/06/06
3900
1、引言
初识JVM指令执行流程
摘要: 记录下学习JVM指令执行流程的理解 正文: 初识JVM指令执行流程 /** * 0: aload_0 * 1: invokespecial #1 // Method java/lang/Object."<init>":()V * 4: return * * @author liugang * @since 2018-04-28 */ public class Example1 { /** * 为主方法创建一个frame并将其推入线程
itliusir
2018/05/21
5030
一文看懂JVM运行时内存分布
繁忙的一年即将过去,由于若干种原因,下定决心开始写一些基础系列,主要包含Java基础、Android基础、设计模式与算法等,目前还没给这个系列想到一个好听的名字。
黄林晴
2021/12/06
2700
一文看懂JVM运行时内存分布
你真的了解For循环吗?一道For循环Java面试题引发的思考
一,疑问 最近群友抛出了一个面试题,就是下图中的第二题,是关于一个for循环的执行结果的问题,他的代码的执行结果是什么呢? 二,代码复现 下面的例子和面试题上面的大同小异,是个非常简单的例子。首先这
架构师小秘圈
2018/04/02
6610
你真的了解For循环吗?一道For循环Java面试题引发的思考
JVM字节码与Java代码层调优
我们都知道,Java源代码不会像C/C++那样直接被编译为机器码,而是被编译成字节码,这造就了Java可以跨平台的特性。JVM实际执行的也是编译后的字节码,所以想要在Java代码层进行调优,就得对字节码有一定的了解。
端碗吹水
2020/09/23
4710
JVM字节码与Java代码层调优
JVM基于栈的解释器执行原理
继上一篇字节码分析finally块对return返回值的影响,好多人对局部变量表和操作数栈之间的关系搞不清楚,下面通过图解来描述局部变量表和操作数栈直接的关系。 通过下面这段代码来解释JVM基于栈的执行原理 4. public static int add(int a, int b) { 5. int c = 0; 6. c = a + b; 7. return c; 8. } 查看字节码的命令:javap -verbose ByteCode.class add方法的字节码如下: public st
java404
2018/05/18
8510
[JVM] JVM自动内存管理机制(一)
文本主要就JVM结构和字节码文件,进行分析来展开JVM的学习,后续系列文章会从JVM的多个方面的进行知识总结。
架构探险之道
2019/09/09
5360
[JVM] JVM自动内存管理机制(一)
JVM - 结合代码示例彻底搞懂Java内存区域_线程栈 | 本地方法栈 | 程序计数器
字节码文件被装载子系统装载到JVM中,字节码执行引擎负责执行这些字节码文件。 装载子系统和执行引擎都是C++的实现。
小小工匠
2021/08/17
3600
手把手教你 javap 反编译分解代码,授人以鱼不如授人以渔
我之前写了一篇关于class文件重要性的,并且从宏观角度解释了下class文件的构成,文章直通车(不直通了,都在这个JVM专辑里面)
阿甘的码路
2020/09/26
5910
手把手教你 javap 反编译分解代码,授人以鱼不如授人以渔
【Java 虚拟机原理】栈帧 | 局部变量表 | 操作数栈 | 方法出口 | JVM 指令逐条解析
" 栈帧 " 中存储的是 局部变量表 , 操作数栈 , 动态链接 , 方法出口 ;
韩曙亮
2023/03/29
4070
【Java 虚拟机原理】栈帧 | 局部变量表 | 操作数栈 | 方法出口 | JVM 指令逐条解析
【jvm】01- java内存结构分析
每一个方法的执行就是一个栈帧,而且在栈内存中遵循先进后出的原理。听到这里,是不是感觉不是很懂(大佬直接忽略)? 我们来看一个示例:
envoke
2020/09/17
5150
【jvm】01- java内存结构分析
2.1 jvm内存模型
Description of Java Conceptual Diagram(java结构)
用户7798898
2020/09/27
4120
2.1 jvm内存模型
Java关键字 Finally执行与break, continue, return等关键字的关系
长文短总结: 在程序没有在执行到finally之前异常退出的情况下,finally是一定执行的,即在finally之前的return语句将在finally执行之后执行。 finally总是在控制转移语句(break,continue,return等)执行之前执行。 可不能小看这个简单的 finally,看似简单的问题背后,却隐藏了无数的玄机。接下来我就带您一步一步的揭开这个 finally 的神秘面纱。 问题分析 首先来问大家一个问题:finally 语句块一定会执行吗? 很多人都认为 finally
老白
2018/03/19
3.9K0
Java关键字 Finally执行与break, continue, return等关键字的关系
i++和++i傻傻分不清楚?这里给你最清楚的解答
本篇文章将介绍——自增变量,这是面试常见的问题,说难不难,说简单也不简单,需要面试者冷静思考,判断正确符号之间的优先级。
wangweijun
2020/02/11
6060
字节码分析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吗?下面我们通过分析下当前方法的字节码,来说明为什么。 查看字节码
java404
2018/05/18
1K0
字节码原理浅析 —— 基于栈的执行引擎
虚拟机常见的实现方式有两种:Stack based 的和 Register based。比如基于 Stack 的虚拟机有Hotspot JVM、.net CLR,这种基于 Stack 实现虚拟机是一种广泛的实现方法。而基于 Register 的虚拟机有 Lua 语言虚拟机 LuaVM 和 Google 开发的安卓虚拟机 DalvikVM。
架构狂人
2023/08/16
5870
字节码原理浅析 —— 基于栈的执行引擎
Javap -c 字节码解析
栈和局部变量操作 将常量压入栈的指令 aconst_null         将null对象引用压入栈 iconst_m1         将int类型常量-1压入栈 iconst_0         将int类型常量0压入栈 iconst_1         将int类型常量1压入栈 iconst_2         将int类型常量2压入栈 iconst_3         将int类型常量3压入栈 iconst_4         将int类型常量4压入栈 iconst_5         将int类
房上的猫
2018/05/17
7230
相关推荐
深入方法区
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验