1. 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
package main
import "fmt"
func main() {
return1()
}
func return1() {
defer func() {
fmt.Println("is defer")
}()
fmt.Println("is return1")
}
运行结果:
is return1
is defer
return之后的语句先执行,defer后的语句后执行
3. 函数的返回值初始化
package main
import "fmt"
func main() {
return1(2)
}
func return1(b int) (i int) {
// b 和 i 对defer这里函数来说,是全局变量
// i 只定义了没有复制所以是0
// b 定义了并且有传值所以是2
defer func() {
fmt.Println(i)
fmt.Println(b)
}()
return 0
}
defer可以调用外层(父级)变量
4. 有名函数返回值遇见defer情况
package main
import "fmt"
func returnButDefer() (t int) { //t初始化0, 并且作用域为该函数全域
defer func() {
// 2、再执行defer里面,此时t已经变成了1,所以最终t = 5
t = t * 5
}()
// 1、先执行return 相当于 i = 1
return 1
}
func main() {
fmt.Println(returnButDefer())
}
5. defer遇见panic,但是并不捕获异常的情况
package main
import (
"fmt"
)
func main() {
defer_call()
// 这里也执行不到了
fmt.Println("main 正常结束")
}
func defer_call() {
defer func() { fmt.Println("defer: panic 之前1") }()
defer func() { fmt.Println("defer: panic 之前2") }()
// 发生异常时,会往上顺序寻找defer并且依次执行,执行所有defer之后,会向向stderr抛出panic信息
panic("异常内容") //触发defer出栈
defer func() { fmt.Println("defer: panic 之后,永远执行不到") }()
}
结果:
defer: panic 之前2
defer: panic 之前1
panic: 异常内容
goroutine 1 [running]:
main.defer_call()
F:/go/study/channel/main.go:16 +0x54
main.main()
6. defer遇见panic,并捕获异常
package main
import (
"fmt"
)
func main() {
defer_call()
fmt.Println("main 正常结束")
}
func defer_call() {
defer func() {
fmt.Println("defer: panic 之前1, 捕获异常")
// 这里使用了recover对异常进行捕获,然后将错误结果打印出来,执行完这个defer之后,整个函数就正真的执行完毕
// 捕获异常了,不会导致整个查询挂掉,会回到main函数继续往下执行
if err := recover(); err != nil {
fmt.Println(err)
}
}()
defer func() { fmt.Println("defer: panic 之前2, 不捕获") }()
// 发生异常时,会往上顺序寻找defer并且依次执行,执行所有defer之后,会向stderr抛出panic信息
panic("异常内容") //触发defer出栈
defer func() { fmt.Println("defer: panic 之后, 永远执行不到") }()
}
输出结果:
defer: panic 之前2, 不捕获
defer: panic 之前1, 捕获异常
异常内容
main 正常结束
recover:是一个Go语言的内建函数,可以让进入宕机流程中的 goroutine 恢复过来,简单来说 recover 就类似php中的 try{}catch(){},捕获异常的,如果不捕获异常,会导致整个程序挂掉
可见defer可以很好的处理程序执行过程中发生的异常
7. defer中包含panic
package main
import (
"fmt"
)
func main() {
defer func() {
// 这个defer 由19行触发
// recover 捕获异常,注意这里只捕获22行抛出的异常
if err := recover(); err != nil {
// 这里会打印 defer panic
fmt.Println(err)
} else {
fmt.Println("fatal")
}
// 最后输出 22行抛出的异常 defer panic 第一个panic已经被第二个panic覆盖了,所以不会有其他操作了
}()
// 1、先会执行这个defer
defer func() {
// 2、里面又发生了panic,会覆盖掉 23行 抛出的异常,接着往上执行defer 第8行
panic("defer panic")
}()
panic("panic")
}
panic仅有最后一个可以被revover捕获。
触发panic(“panic”)后defer顺序出栈执行,第一个被执行的defer中 会有panic(“defer panic”)异常语句,这个异常将会覆盖掉main中的异常panic(“panic”),最后这个异常被第二个执行的defer捕获到。
8. defer函数参数包含子函数
package main
import "fmt"
func function(index int, value int) int {
fmt.Println(index)
return index
}
func main() {
// defer 入栈的时候,需要连同函数地址、函数形参一同进栈,
// 那么为了得到function1的第二个参数的结果
// 所以就需要先执行function3将第二个参数算出,那么function3就被第一个执行
// 先把参数计算出来,再压栈 所以,先输出 3 4
defer function(1, function(3, 0))
defer function(2, function(4, 0))
// 函数执行完,往上执行defer,输出 2 1
}
前几个点都是从函数执行完之后的角度去思考defer的,想上面这个例子得从defer入栈角度去思考