defer、异常处理、import
目录:
1:defer延迟执行
2:
3:import规则
一、defer延迟执行
Go语言的 defer 语句会将其后面跟随的语句进行延迟处理
defer特性:
-
关键字 defer 用于注册延迟调用。
-
这些调用直到 return 前才被执。因此,可以用来做资源清理。
-
多个defer语句,按先进后出的方式执行。
-
defer语句中的变量,在defer声明时就决定了。
defer的用途:
-
关闭文件句柄
-
锁资源释放
-
数据库连接释放
go 语言的defer功能强大,对于资源管理非常方便,但是如果没用好,也会有陷阱。
案例1:统一函数执行耗时时间是多少

-
-
调用 defer 关键字会
立刻拷贝函数中引用的外部参数,包括start 和time.Since中的Now -
defer的函数在
压栈的时候也会保存参数的值,并非在执行时取值
package main
import (
"log"
"time"
)
func main() {
start := time.Now()
log.Printf("开始时间为:%v", start)
defer func() {
log.Printf("开始调用defer")
log.Printf("时间差:%v", time.Since(start))
log.Printf("结束调用defer")
}()
time.Sleep(3 * time.Second)
log.Printf("函数结束")
}
因为拷贝的是函数指针,函数属于引用传递
案例2:按顺序打印数值
package main
import "fmt"
func main() {
var whatever = [5]int{1,2,3,4,5}
for i,_ := range whatever {
//函数正常执行,由于闭包用到的变量 i 在执行的时候已经变成4,所以输出全都是4.
defer func() { fmt.Println(i) }()
}
}
解决办法:
package main
import "fmt"
func main() {
var whatever = [5]int{1,2,3,4,5}
for i,_ := range whatever {
i := i //将i进行重新赋值,每个i都开辟一个自己的内存空间
defer func() { fmt.Println(i) }()
}
}
二、异常处理
Go语言中使用 panic 抛出错误,recover 捕获错误。
panic:
-
-
假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行
-
返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行
-
直到goroutine整个退出,并报告错误
recover:
-
-
用来捕获panic,从而影响应用的行为
golang 的错误处理流程:当一个函数在执行过程中出现了异常或遇到 panic(),正常语句就会立即终止,然后执行 defer 语句,再报告异常信息,最后退出 goroutine。
如果在 defer 中使用了 recover() 函数,则会捕获错误信息,使该错误信息终止报告。
注意:
-
利用recover处理panic指令,defer 必须放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。
-
recover 处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点。
-
多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用。
package main
func main() {
test()
}
func test() {
defer func() {
if err := recover(); err != nil {
println(err.(string)) // 将 interface{} 转型为具体类型。
}
}()
panic("panic error!")
}
func panic(v interface{})
func recover() interface{}
package main
import "fmt"
func test() {
defer func() {
// defer panic 会打印
fmt.Println(recover())
}()
defer func() {
panic("defer panic")
}()
panic("test panic")
}
func main() {
test()
}
package main
import "fmt"
func test(x, y int) {
var z int
func() {
defer func() {
if recover() != nil {
z = 0
}
}()
panic("test panic")
z = x / y
return
}()
fmt.Printf("x / y = %d\n", z)
}
func main() {
test(2, 1)
}
type error interface {
Error() string
}
标准库 errors.New 和
package main
import (
"errors"
"fmt"
)
var ErrDivByZero = errors.New("division by zero")
func div(x, y int) (int, error) {
if y == 0 {
return 0, ErrDivByZero
}
return x / y, nil
}
func main() {
defer func() {
fmt.Println(recover())
}()
switch z, err := div(10, 0); err {
case nil:
println(z)
case ErrDivByZero:
panic(err)
}
}
Go实现类似 try catch 的异常处理:
package main
import "fmt"
func Try(fun func(), handler func(interface{})) {
defer func() {
if err := recover(); err != nil {
handler(err)
}
}()
fun()
}
func main() {
Try(func() {
panic("test panic")
}, func(err interface{}) {
fmt.Println(err)
})
}
如何区别使用 panic 和 error 两种方式?
惯例是:导致关键流程出现不可修复性错误的使用 panic,其他使用 error。
三、import


浙公网安备 33010602011771号