面试题思考:try 代码块中含 return 语句时,代码执行顺序

刷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;
        }
    }
  1. 当代码执行到return t;时,并不是直接将t返回了出去,而是将t保留了起来(因为还有一个finally语句块没有执行!)
    并且这个保留,就是值传递性质的一个保留,也就是保留的是t的一个副本(复制体),我这里先叫他tt吧(不是套套!!)
  1. 接下来执行finally语句块,finally中将t做了自增运算,t的确变成了1,但是这并没有影响t的复制体tt的值!保留起来的tt值还是0!
  2. 这个时候执行完了finally,正式将保留起来的tt返回出去,于是,整个函数的返回结果就是0
  3. 这个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才能执行完毕。

全文下来,真是用我的三寸不烂之舌经过滔滔不绝的输出连绵不绝的蛊惑打开了你的新世界大门啊

结语

更多内容欢迎访问我的主页我的博客
如果我的文章确实有帮助到你,请不要忘了点一下文末的"♡"让他变成"❤"
作为一直雏鸡难免很多地方理解不到位,文中若有错误请直(bu)接(yao)指(ma)出(wo)
写作不易!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,497评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 小编费力收集:给你想要的面试集合 1.C++或Java中的异常处理机制的简单原理和应用。 当JAVA程序违反了JA...
    八爷君阅读 4,544评论 1 114
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,028评论 0 62
  • 本文参加#感悟三下乡,青春筑梦行#活动,本人承诺,文章内容为原创,且未在其他平台发表过。 我们是安徽师范大学生命科...
    oh窗外阅读 210评论 0 0