defer 基础知识回顾
defer 作用:
defer
是一种延迟执行机制,它确保在函数即将退出前执行特定的操作,如释放资源(如关闭数据库连接或文件流)。执行顺序:
defer
遵循栈的后进先出(LIFO)原则,即先定义的defer
语句会在函数退出时最后执行,后定义的defer
语句则最先执行。与 return 的关系:尽管
defer
在函数返回之前执行,但return
本身分为两步:首先计算返回值,然后执行defer
语句,最后完成函数的返回。
defer 相关知识点总结
1. defer 执行顺序
defer
的执行顺序严格按照它们在代码中定义的逆序进行。
2. defer 与 return 的先后顺序
通过实例代码演示,我们发现尽管defer
总是在return
之前执行,但在return
语句开始执行前,会先完成返回值的赋值操作。
3. 函数返回值初始化与 defer 的间接影响
在函数返回前,defer
可以修改返回值,最终函数实际返回的是经过defer
修改后的值。
4. defer 遇见 panic
当函数中发生panic
时,先前定义的所有defer
仍按顺序执行。
5. defer 中包含 panic
即使defer
内的匿名函数触发了panic
,defer
依然会按顺序执行,并且defer
能够捕获到panic
的信息。
6. defer 下的函数参数包含子函数
在defer
表达式中调用带有子函数的参数时,会先执行子函数以获取其结果值,然后按照defer
的栈顺序依次执行。
示例解析
示例一:defer 和 return 结合使用
package main
import "fmt"
func main() {
function1()
}
func function1() {
fmt.Printf("1")
defer function2()
fmt.Printf("2")
}
func function2() {
fmt.Printf("3")
}
// 输出结果:
// 1 2 3
示例二:defer 修改返回值
func demo3() (a int) {
defer func() {
a = 3
}()
return 1
}
// 输出结果:
// 3
示例三:局部变量值拷贝对defer的影响
func demo7() int {
var a int
defer func(a int) {
a = 10
}(a)
return 2
}
// 输出结果:
// 10 2
这里解释了为何demo7
函数返回10
而非2
:由于在执行defer
时传入的是a
的副本,而不是引用,所以修改副本并不会影响原始变量a
的值。如果传递的是指向a
的指针,那么结果将会不同。
示例四:defer 遇到 panic
func demo4() {
defer func() {
fmt.Println("1")
}()
defer func() {
fmt.Println("2")
}()
panic("panic")
defer func() {
fmt.Println("3")
}()
defer func() {
fmt.Println("4")
}()
}
// 输出结果:
// 2 1 panic: panic
// goroutine 1 [running]:
// main.demo4()
// 分析:
// 即使发生了panic,仍然执行了部分defer语句。由于panic导致程序中断,后续的defer未被执行。
示例五:defer 中使用 recover 处理 panic
func demo5() {
defer func() {
fmt.Println("1")
if err := recover(); err != nil {
fmt.Println(err)
}
}()
defer func() { fmt.Println("2") }()
panic("panic")
// 不会被执行的defer语句...
}
// 输出结果:
// 2 1 panic
总结来说,defer
的使用虽简单,但仍需注意以下几点:
defer
在函数返回前执行,其执行顺序优于return
,且return
包含两步操作:先确定返回值,然后执行defer
,最后执行return
返回调用者。即使函数内部出现
panic
异常,panic
发生前已定义的defer
仍然会被执行。若
defer
内包含子函数调用,子函数会按defer
定义的顺序先行执行,而整个defer
逻辑则是按照栈的顺序执行。