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