Go语言中defer
说finally
之前我们先说说Go语言中defer
Go语言中的defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。
在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer语句执行的时机就在返回值赋值操作后,RET指令执行前。具体如下图所示:
这里先看一个例子:
package main
import "fmt"
func demo() int {
i := 10
defer func() {
i = 20
}()
return i
}
func main() {
fmt.Println(demo()) // 10
}
执行上面代码的时候相当于执行下面步骤:
- 返回值 = i = 10
- 执行
defer
中的函数:i = 20 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]
}
同样,相当于执行以下操作:
- 返回值 =
[hello]
- 执行
defer
中的函数:sli = [hello,world]
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修改之后的值,事实上还是执行了以下步骤:
- 返回值 =
p
的地址 - 执行
defer
中的函数: 修改p
的地址对应PersonA
结构体 - 返回
p
的地址
这时候再打印p地址对应的对象,显然是我们已经修改过的对象。
java中的finally
java中的finally
和return
结合之后执行的顺序和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
}
}
这里相当于:
- 返回值 = i = 20
- 执行
finally
语句:i = 30 - 将返回值(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
}
}
执行顺序:
- 返回值 =
sb
的地址 - 执行
finally
:修改sb
对应地址的对象 - 返回
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
的两种情况:- 在
try
语句之前有return
语句 - 程序中有
System.exit();
语句
- 在
- 根据上面的例子,如果
finally
和catch
中都有return
语句的话,在catch
中return
语句没有返回的时候,finally
中的return
语句已经返回了,所以finally
中的return
语句会覆盖catch
中的return
语句
参考文章:
https://www.liwenzhou.com/posts/Go/09_function/
https://mp.weixin.qq.com/s/yrwYkEQuD1t1iWtOuWyZ6w