文章目录
1.defer关键字
2.defer特性、用途
3.painc内置函数
func panic(v interface{})
4.recover内置函数
func recover() interface{}
5.defer在ReadFile函数内关闭句柄
//比如我们看 Go 内置的 io/ioutil 包提供的读取文件方法 ReadFile 实现源码,其中就有 defer 语句的使用:
func ReadFile(filename string) ([]byte, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
var n int64 = bytes.MinRead
if fi, err := f.Stat(); err == nil {
if size := fi.Size() + bytes.MinRead; size > n {
n = size
}
}
return readAll(f, n)
}
6.defer在闭包中的应用(捕获变量被初始化赋值且修改)
7.先return再defer
8.defer在遍历执行结构体方法中的应用
9.defer在遍历执行函数中的应用
10.defer遍历切片输出索引
11.自发panic
//底层抛出 panic
package main
import "fmt"
func main() {
var i = 1
var j = 0
var k = i / j
fmt.Printf("%d / %d = %d\n", i, j, k)
}
/*
panic: runtime error: integer divide by zero
goroutine 1 [running]:
main.main()
D:/all project/go/demo/main/1.go:8 +0x11
*/
12.使用defer自定义错误输出在panic之前
//在这段代码中,我们定义了两个 defer 语句,并且是在函数最顶部,以确保异常情况下也能执行。
/*在函数正常执行的情况下,这两个 defer 语句会在最后一条打印语句执行完成后先执行第二条 defer 语句,再执行第一条 defer 语句:
*/
package main
import "fmt"
func printError() {
fmt.Println("兜底执行")
}
func main() {
defer printError()
defer func() {
fmt.Println("除数不能是0!")
}()
var i = 1
var j = 1
var k = i / j
fmt.Printf("%d / %d = %d\n", i, j, k)
}
/*输出以下三行:
1 / 1 = 1
除数不能是0!
兜底执行
*/
//底层抛出 panic
//而如果我们把 j 的值设置为 0,则函数会抛出 panic:
/*表示除数不能为零。这个时候,由于 defer 语句定义在抛出 panic 代码的前面,所以依然会被执行,底层的逻辑是在执行 var k = i / j 这条语句时,遇到除数为 0,则抛出 panic,然后立即中断当前函数 main 的执行(后续其他语句都不再执行),并按照先进后出顺序依次执行已经在当前函数中声明过的 defer 语句,最后打印出 panic 日志及错误信息*/
package main
import "fmt"
func printError() {
fmt.Println("兜底执行")
}
func main() {
defer printError()
defer func() {
fmt.Println("除数不能是0!")
}()
var i = 1
var j = 0
var k = i / j
fmt.Printf("%d / %d = %d\n", i, j, k)
}
/*
除数不能是0!
兜底执行
panic: runtime error: integer divide by zero
goroutine 1 [running]:
main.main()
D:/all project/go/demo/main/1.go:20 +0x46
*/
13.使用panic
15.使用recover
/*没有通过 recover() 函数捕获 panic 的话,程序会直接崩溃退出,并打印错误和堆栈信息,但现在我们在 divide() 函数的 defer 语句中通过 recover() 函数捕获了 panic,并打印捕获到的错误信息,这个时候,程序会退出 divide() 函数而不是整个应用,继续执行 main() 函数中的后续代码,即恢复后续其他代码的执行:
*/
package main
import (
"fmt"
)
func divide() {
defer func() {
if err := recover(); err != nil {
fmt.Printf("Runtime panic caught: %v\n", err)
}
}()
var i = 1
var j = 0
k := i / j
fmt.Printf("%d / %d = %d\n", i, j, k)
}
func main() {
divide()
fmt.Println("divide 方法调用完毕,回到 main 函数")
}
/*输出:
Runtime panic caught: runtime error: integer divide by zero
divide 方法调用完毕,回到 main 函数
*/
/*如果在代码执行过程中没有抛出 panic,比如我们把 divide() 函数中的 j 值改为 1,则代码会正常执行到函数末尾,然后调用 defer 语句声明的匿名函数,此时 recover() 函数返回值为 nil,不会执行 if 分支代码,然后退出 divide() 函数回到 main() 函数执行后续代码*/
package main
import (
"fmt"
)
func divide() {
defer func() {
if err := recover(); err != nil {
fmt.Printf("Runtime panic caught: %v\n", err)
}
}()
var i = 1
var j = 1
k := i / j
fmt.Printf("%d / %d = %d\n", i, j, k)
}
func main() {
divide()
fmt.Println("divide 方法调用完毕,回到 main 函数")
}
/*输出:
1 / 1 = 1
divide 方法调用完毕,回到 main 函数
*/