Golang Defer 知识点

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入栈角度去思考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值