面试题之golang中的defer
一、题目
趋势科技一面golang软开实习
面试官:说一下你对defer的理解和使用注意事项
二、defer示例
1、defer执行顺序
多个defer出现,前后执行呈栈的关系,先进后出,程序流程中前面的defer比后面的defer调用的晚。另外defer后边只能跟函数。
package main
import "fmt"
func main() {
defer func1()
defer func2()
defer func3()
}
func func1() {
fmt.Println("A")
}
func func2() {
fmt.Println("B")
}
func func3() {
fmt.Println("C")
}
执行结果
C
B
A
2、defer与return
return之后的语句先执行,defer后的语句后执行
package main
import "fmt"
func deferFunc() {
fmt.Println("defer func called")
}
func returnFunc() {
fmt.Println("return func called")
}
func returnAndDefer() int {
// 后执行
defer deferFunc()
// 先执行
return returnFunc()
}
func main() {
returnAndDefer()
}
执行结果
return func called
defer func called
3、defer与无命名返回值函数
如果函数的返回值是无名的(不带命名返回值),则go语言会在执行return的时候会执行一个类似创建一个临时变量作为保存return值的动作。
package main
import "fmt"
//无命名返回值
func test() int {
var i int
defer func() {
i++
//作为闭包引用的话,则会在defer函数执行时根据整个上下文确定当前的值。i=2
fmt.Println("defer1", i)
}()
defer func() {
i++
//作为闭包引用的话,则会在defer函数执行时根据整个上下文确定当前的值。i=1
fmt.Println("defer2", i)
}()
// 先执行return i, 把i的值给到一个临时变量,作为函数返回值
return i
}
func main() {
// defer 和 return之间的顺序是先返回值, i=0,后defer
fmt.Println("test: ", test())
}
执行结果
defer2 1
defer1 2
test: 0
执行顺序为return语句->defer2->defer1->返回值。defer2先于defer1执行
因此执行逻辑可以看做:
return先执行,负责把结果写入返回值中,接着多个defer按照先进后出的顺序开始调用执行一些收尾工作,最后函数携带这个返回值退出。
一般认为函数中执行到return,就直接函数生命周期结束,return的返回值就是函数返回值。但是由于defer语句的存在,return执行可以看做分为了两个步骤:
- 赋值:由于返回值没有命名,所以默认指定了一个临时变量,比如tmp:=i
- 返回:真正函数返回的是tmp,而后续defer语句对i的修改,不会影响到tmp
4、defer与命名返回值函数
命名返回值的函数,由于返回值在函数定义的时候已经将该变量进行定义,在执行return的时候会先执行返回值保存操作,而后续的defer函数会改变这个返回值,虽然defer是在return之后执行的,但是由于使用的函数定义的这个变量,所以执行defer操作后对该变量的修改,进而最终会影响到函数返回值。
package main
import "fmt"
func test() (i int) { //返回值命名i
defer func() {
i++
fmt.Println("defer1", i)
}()
defer func() {
i++
fmt.Println("defer2", i)
}()
return i
}
func main() {
fmt.Println("test:", test())
}
执行结果:
defer2 1
defer1 2
test: 2
defer1,defer2的时候都可以修改变量i
5、defer与panic
正常情况下,defer遇到return或者函数执行流程到达函数体末尾会将进入栈的defer出栈并以此执行,同样遇到panic语句也是。
遇到panic的时候,会遍历并将已经进栈的defer出栈并执行,但是对于程序流程中panic之后的defer就不会进栈。在defer出栈执行的过程中,遇到recover则停止panic,如果没有recover捕获panic,则执行完所以defer之后,抛出panic信息。
package main
import (
"fmt"
)
func test() {
defer func() { fmt.Println("defer: panic 之前0, 不捕获") }()
defer func() {
fmt.Println("defer: panic 之前1, 捕获异常")
// 捕获异常信息
if err := recover(); err != nil {
// 输出panic中的错误信息
fmt.Println(err.(string))
}
}()
// 正常进栈
defer func() { fmt.Println("defer: panic 之前2, 不捕获") }()
//触发defer出栈
panic("触发异常")
// 由于在panic之后,不会在执行
defer func() {
fmt.Println("defer: panic 之后, 永远执行不到")
}()
}
func main() {
test()
// 由于存在recover捕获panic,main函数流程则正常执行
fmt.Println("main 正常结束")
}
执行结果
defer: panic 之前2, 不捕获
defer: panic 之前1, 捕获异常
触发异常
defer: panic 之前0, 不捕获
main 正常结束
牛客网相关题目:体温异常
posted on 2022-05-31 15:41 weilanhanf 阅读(525) 评论(2) 编辑 收藏 举报