java面试题之try中含return语句时代码的执行顺序详解
作者:LiuZh 发布时间:2023-11-24 07:34:16
前言
最近在刷java面试题偶然看到这类问题(try/finally中含有return时的执行顺序),觉得挺有意思于是小小的研究了一下,希望经过我添油加醋天马行空之后,能给你带来一定的帮助,下面来看看详细的介绍。
原题
try {} 里有一个return语句,那么紧跟在这个try后的finally {}里的代码会不会被执行?什么时候被执行?在return前还是后?
乍一看题目很简单嘛,java规范都说了,finally会在try代码块的return之前执行,你这文章写得没意义,不看了
你等等!(拿起我身边的五尺砍刀)
神奇栗子
看完这个栗子,你在想想执行顺序到底是怎样的
栗子代码
public static void main(String[] args) {
int result = test();
System.out.println(result);
}
public static int test() {
int t = 0;
try {
return t;
} finally {
++t;
}
}
分析一下
test()
方法内,在try中return了t,那么在main方法中test()
函数的返回值应该是t=0,即控制台输出0
但是因为有finally的存在,而finally中对t进行了自增运算,并且finally会在try中的return语句之前执行,所以正确的情况是控制台输出1
所以你最终确定的答案是:控制台输出1
然而事实并非如此,将程序跑起来之后,得到的结果是:
输出0
将栗子跑起来亲眼看一下吧~
得到这个结果你也许要 * 了,啥?java规范说的都是错的?!
不用急,到我给sun洗地的时间了
洗地时间
在洗地之前,你很有必要先理解java中的值传递,如果你已经了解该内容可略过下面这一个小节点
java中的值传递
由于这只是本文内容引申出去的知识点,不过多赘述,随便唠两句,能借此明白则好,不明白希望借助搜索引擎明白一下!
在java的方法调用中,时常需要传递参数,那么传递的参数是将之前的变量直接传递给方法内了吗?
显然不是的,调用方法传递参数的时候,传递的只是原变量的一个副本(复制体),换句话说就是,将变量的值传递给了方法体,而并没有真正的将变量传递进去。
看个栗子:
public static void main(String[] args) {
int t = 0;
test(t);
System.out.println(t);
}
public static int test(int a) {
a = 111;
}
正确输出是0,因为test()
方法内拿到的a,只是t的一个副本(复制体)而并不直接是t,test()
内改变了a的值,并不影响t的值
以上是对于基本数据类型,如果对于对象呢?
如果参数是对象,那么传递的是对象的引用的副本(复制体),这也就是为什么在方法体内对对象进行修改,会真正的改变对象。因为方法体外的引用和方法体内的引用指向的是堆内存中的同一个对象,传递的是对象的引用
如果这里还不能理解值传递,建议先理解一下这一个概念再继续往下看
真的开始分析了
为了你看着方便,栗子代码再来一份:(我真的不是为了凑字数)
public static void main(String[] args) {
int result = test();
System.out.println(result);
}
public static int test() {
int t = 0;
try {
return t;
} finally {
++t;
}
}
当代码执行到return t;时,并不是直接将t返回了出去,而是将t保留了起来(因为还有一个finally语句块没有执行!)并且这个保留,就是值传递性质的一个保留,也就是保留的是t的一个副本(复制体),我这里先叫他tt吧(不是套套!!)
接下来执行finally语句块,finally中将t做了自增运算,t的确变成了1,但是这并没有影响t的复制体tt的值!保留起来的tt值还是0!
这个时候执行完了finally,正式将保留起来的tt返回出去,于是,整个函数的返回结果就是0
这个t的副本(复制体)保留的地方是哪儿呢?我查了半天,有个应该靠谱的说法,保留在函数栈中,但具体保留的区域叫什么,我也不知道,还请知情大佬指教一下!
上图或许直观一点?
那么如果,这个t是一个对象呢?按照前面说的值传递的问题,如果t是一个对象,在finally中对t进行修改,那么最终返回出去的t所显示出来的数据,应该是经过修改的。
写一个Person类来检验一下吧
public class Test {
public static void main(String[] args) {
Person result = test();
System.out.println(result.age);
}
public static Person test() {
Person t = new Person();
t.age = 0;
try {
return t;
} finally {
t.age++;
}
}
}
class Person {
int age;
}
这段代码输出的是1,因为Person是一个类,t是一个对象的引用,对象实例保存在堆内存中,t的副本tt也是一个对象的引用,t和tt都指向堆内存中的对象实例,那么不论修改谁,实际上对象实例都被修改了!
看完我这一通胡说八道,你应该了解了整个执行流程咯?
那么继续开一个引申
又一个小栗子
如果在finally中也有一个return,会发生什么?
public static void main(String[] args) {
int result = test();
System.out.println(result);
}
public static int test() {
int t = 0;
try {
return t;
} finally {
++t;
return t;
}
}
最终输出的结果是1
就是说,如果try中有return而finally中也有return,那么后者将会让前者失效!
理解
=> try中将t保留了一份副本用于返回出去,到了finally中,又有一个return语句,这时候又要创建一个用于返回的副本,那这个时候就有两个副本了,到底返回谁呢?取后者!
总结
这一个面试题,看似简单,却暗藏杀机啊!
可是说了这么多,结果就是finally在return之后执行吗?
非也,你没看见return没有真正的执行完就开始执行finally吗?并且是先执行完了finally,才执行完return,这也就很好理解java规范中的finally在return之前执行了。
不过,按如上情况,这句话应该变成这样:finally比return先执行完毕。是不是就更容易理解了呢?
也就是说,return先被执行了,执行return的时候发现有finally,于是不能那么快执行完毕return,先去执行finally,等finally执行完毕之后,return才能执行完毕。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家学习或者工作能带来一定的帮助.
来源:https://juejin.im/post/5901cefa44d90400690cd3d4


猜你喜欢
- maven运行依赖于 JAVA_HOME如果各位还没有配置 JAVA_HOME,可以参考我的另一篇博客 JDK环境变量配置 JDK 环境变量
- 前言快捷方式是一种特殊的文件,扩展名为 lnk。有很多种方式来创建快捷方式,首先我们看一下快捷方式是什么。对快捷方式点右键,选择属性菜单,在
- Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示。本文从Queue接口函数出发,结合生动的图解,深入浅出地分
- 顺序语句顺序顾名思义就是程序自上而下执行public class User { public static voi
- 前言在实际的开发过程中,我们经常修改代码之后,手动的重启项目,查看修改效果。那么有没有一种方式能够快速的、自动的帮我们将修改代码自动更新,避
- 这篇文章主要讲述服务追踪组件zipkin,Spring Cloud Sleuth集成了zipkin组件。一、简介Add sleuth to
- Android SQLite数据库版本升级的管理实现我们知道在SQLiteOpenHelper的构造方法:super(Context con
- using System;using System.Collections.Generic;namespace Demo{ &nb
- 其实这个http下载器的功能已经相当完善了,支持:限速、post投递和上传、自定义http header、设置user agent、设置ra
- Rxjava功能个人感觉很好用,里面的一些操作符很方便,Rxjava有:被观察者,观察者,订阅者,被观察者通过订阅者订阅观察者,从而实现观察
- MyBatis根据条件批量修改字段背景:给学生改作业,只要是对的都批量进行数据库的修改代码以及注释conttoller@RestContro
- using System;using System.Collections.Generic;using System.ComponentMo
- * 的工作原理如图 * 是由每一个action请求(request)都包装在一系列的 * 的内部,通过redirectAction再一次
- 这篇讲解一下rocketMq的事务消息的原理在发送事务消息的时候,会加一个标识,表示这个消息是事务消息。broker接收到消息后,在我们之前
- 一、简介 1、地图 地图展示:普通地图(2D,3D)、卫星图和实时交通图。 地图操作:可通过接口或手势控制来实
- 一、文件的编码package com.study.io;/*** 测试文件编码*/public class EncodeDemo {/***
- Google的在Google I/O大会上推出了一款新的开发工具android studio。这是一款基于intellij IDE的开发工具
- 最近有一个项目要用到年份周期,用于数据统计图表展示使用,当中用到年份周期,以及年份周期所在的日期范围。当初设想通过已知数据来换算年份周期,经
- 补充知识:正定矩阵奇异矩阵严格对角占优要理解Gauss消去法,首先来看一个例子:从上例子可以看出,高斯消去法实际上就是我们初中学的阶二元一次
- 需要注意的一点是,feign好像是无法传递list集合类型的,但是你可以通过传递对象类型,然后在接收方再次将对象装在集合中达到集合传递的效果