标签:defer recover 异常 fmt func import main panic
目录:
1:defer延迟执行
2:异常处理
3:import规则
一、defer延迟执行
Go语言的 defer 语句会将其后面跟随的语句进行延迟处理
defer特性:
-
关键字 defer 用于注册延迟调用。
-
这些调用直到 return 前才被执。因此,可以用来做资源清理。
-
多个defer语句,按先进后出的方式执行。
-
defer语句中的变量,在defer声明时就决定了。
defer的用途:
-
关闭文件句柄
-
锁资源释放
-
数据库连接释放
go 语言的defer功能强大,对于资源管理非常方便,但是如果没用好,也会有陷阱。
案例1:统一函数执行耗时时间是多少
-
Go 语言中所有的
函数调用都是传值的
-
调用 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 捕获错误。
异常的使用场景简单描述:Go中可以抛出一个panic的异常,然后在defer中通过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!") }
由于 panic、recover 参数类型为 interface{},因此可抛出任何类型对象。
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) }
除用 panic 引发中断性错误外,还可返回 error 类型错误对象来表示函数调用状态:
type error interface { Error() string }
标准库 errors.New
和 fmt.Errorf
函数用于创建实现 error 接口的错误对象。通过判断错误对象实例来确定具体错误类型。
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
标签:defer,recover,异常,fmt,func,import,main,panic 来源: https://www.cnblogs.com/hero799/p/15954487.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。