java中finally和return的执行顺序(对比Go语言中defer)

Go语言中defer

finally之前我们先说说Go语言中defer

Go语言中的defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。

在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer语句执行的时机就在返回值赋值操作后,RET指令执行前。具体如下图所示:
defer执行顺序
这里先看一个例子:

package main

import "fmt"

func demo() int {
	i := 10
	defer func() {
		i = 20
	}()
	return i
}
func main() {
	fmt.Println(demo()) // 10
}

执行上面代码的时候相当于执行下面步骤:

  1. 返回值 = i = 10
  2. 执行defer中的函数:i = 20
  3. return返回值(此时的i为20,但是返回值是10)

假设对切片类型进行和上面类似的操作:

package main

import "fmt"

func demo() []string {
	var sli []string
	sli = append(sli, "hello")
	defer func() {
		sli = append(sli, "world")
	}()
	return sli
}
func main() {
	fmt.Println(demo()) // [hello]
}

同样,相当于执行以下操作:

  1. 返回值 = [hello]
  2. 执行defer中的函数:sli = [hello,world]
  3. return返回值

这里注意,每次执行append之后返回的切片地址都不同

假如我们自定义类型,然后对指针进行操作:

package main

import (
	"fmt"
)

type PersonA struct {
	name string
	age  int
}

func test() *PersonA {
	p := &PersonA{"小明", 18}
	defer func() {
		p.name = "小红"
	}()
	return p
}
func main() {
	fmt.Println(test()) // &{小红 18}
}

可见,运行结果为defer修改之后的值,事实上还是执行了以下步骤:

  1. 返回值 = p的地址
  2. 执行defer中的函数: 修改p的地址对应PersonA结构体
  3. 返回p的地址

这时候再打印p地址对应的对象,显然是我们已经修改过的对象。

java中的finally

java中的finallyreturn结合之后执行的顺序和Go语音中的defer类似

class MyExceptionDemo {
  public static void demo() {
    throw new RuntimeException();
  }

  public static int test() {
    int i = 10;
    try {
      demo();
      return i;
    } catch (Exception e) {
      i = 20;
      return i;
    } finally {
      i = 30;
    }
  }

  public static void main(String[] args) {
    System.out.println(test()); // 20
  }
}

这里相当于:

  1. 返回值 = i = 20
  2. 执行finally语句:i = 30
  3. 将返回值(20)返回

上面的是基本数据类型,如果是引用类型的话:

class ExceptionDemo {
  public static void demo() {
    throw new RuntimeException();
  }

  public static StringBuffer test() {
    StringBuffer sb = new StringBuffer();
    try {
      demo();
      return sb;
    } catch (final Exception e) {
      sb.append("do catch ");
      return sb;
    } finally {
      sb.append("do finally ");
    }
  }

  public static void main(final String[] args) {
    System.out.println(test()); // do catch do finally
  }
}

执行顺序:

  1. 返回值 = sb的地址
  2. 执行finally:修改sb对应地址的对象
  3. 返回sb的地址

finally中有return语句的时候,finally中语句会覆盖try里的语句

public static void main(String[] args) {
    System.out.println(test2()); // finally
}

public static String test2() {
    String str;
    try {
        str = "try";
        return str;
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        return "finally";
    }
}

finally使用过程中需要注意的地方

  • 不会执行finally的两种情况:
    1. try语句之前有return语句
    2. 程序中有System.exit();语句
  • 根据上面的例子,如果finallycatch中都有return语句的话,在catchreturn语句没有返回的时候,finally中的return语句已经返回了,所以finally中的return语句会覆盖catch中的return语句

参考文章:
https://www.liwenzhou.com/posts/Go/09_function/
https://mp.weixin.qq.com/s/yrwYkEQuD1t1iWtOuWyZ6w

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值