golang之资源释放/异常错误处理解析
目录
- 资源释放-defer
- 关于 defer 的一些常见误区和陷阱
- defer与闭包陷阱
- try-catch-finaandroidlly
- 关于内建函数 recover
- EOF - 错误处理
资源释放-defer
类似于其他语言的 析构函数 比如php的 __destruct
func b() error { resp, err := http.Get("xxx.com") // 先判断操作是否成功 if err != nil { return err } // 如果操作成功,再进行Close操作 defer resp.Body.Close() return nil }
从上面的示例可看到,defer
是个延迟函数(延迟执行)
关于 defer 的一些常见误区和陷阱
defer 语句是用来延迟执行函数的,且延迟发生在函数 return 之后
具体指: Go return 在底层等于: www.devze.com返回值入栈, defer函数调用, return
fphpunc returnValues() int { var result int defer func() { // defer 匿名函数对变量的修改发生在 return 之后 result++ fmt.Println("defer") }() return result } // 如下输出 0 fmt.Println(returnValues())
go允许多个defer同时存在
defer 执行顺序遵循先进后出 first-in-last-out (FILO)属于栈操作,如下输出:3、2、1
func stackingDefers() { defer func() { fmt.Println("1") }() defer func() { fmt.Println("2") }() defer func() { fmt.Println("3") }() }
函数被 deferred 时涉及的 参数变量值 在 defer 函数编写时确定,而非实际调用时
func c() { i := 0 defer fmt.Println(i) // 此时i变量值已被确定,即0 i++ defer fmt.Println(i) // 此时i++已执行,变量值已被确定为 1 } // 输出 1 0;因为defer的执行顺序为 后进先出,所以先输出 i++ 之后的 1;再输出之前的 0
defer与闭包陷阱
先来看一个示例:
func main() { i := 1 defer func() { fmt.Println("defer-闭包i值:", i) }() defer fmt.Println("defer-i值:", i) i += 100 }
结果输出为:
defer-i值: 1
defer-闭包i值: 101
为啥捏?
- 第二个defer,因为函数被 deferred 时涉及的 参数变量值 在 defer 函数编写时已确定,所以输出
defer-i值: 1
- 第一个defer,闭包中的参数传递是引用类型传递,因此匿名函数中的 i 可以输出 101(匿名函数都是闭包,按引用类型传递)
try-catch-finally
Go追求简洁优雅,所以不支持传统的 try-catch-finally 结构,因为Go语言的设计者们认为,将异常与控制结构混在一起很容易使得代码变混乱(咱不懂,但内心大受震撼)。
如果要在go实现这么个逻辑,需要用到这么几个关键词:defer, panic, recover
如何使用 defer 执行 try-catch-finally 调试:
func main() { // 类似其他语言的 try (隐式包含) defer func() { // 捕获异常:类似 catch(\Exception $e) if err := recover(); err != nil { 编程客栈 // 输出异常 fmt.Println(err) // 打印错误堆栈: 类似于PHP的 debug_print_backtrace debug.PrintStack() // 输出到日志即可 fmt.Println() fmt.Println("执行:planB!") planB() } }() // 注意!!用recover处理panic指令,defer 须在 panic 之前声明,否则panic时会直接退出,recover无法捕获到panic tryCatchFinally() fmt.Println("dadada...") // 这里不会执行 } func tryCatchFinally() { fmt.Println("begin") panic("抛出异常信息: throw new exception") fmt.Println("end") // 这里不会执行 } func planB() { fmt.Println("exec-planB, done!!") }
关于内建函数 recover
1、用来控制一个 goroutine 的 panicking 行为,捕获panic,从而影响应用的行为
2、一般调用方式- 在defer函数中,通过 recover 来终止一个 gojroutine 的 panicking 过程,从而恢复正常逻辑的执行
- 获取通过panic传递的error,执行捕获之后的逻辑,参考传统tryCatchFinally
EOF - 错误处理
另一种常见错误处理,即 EOF(End-Of-File)文件结尾错误,可以直接捕获,示例如下:
reader := strings.NewReader("clear is better than clever") p := make([]byte, 5) for { js n, err := reader.Read(p) if err != nil { if err == io.EOF { fmt.Println("EOF:", n) } else { fmt.Println(err) } os.Exit(1) } // 注: p 每次循环都会被覆盖,但如果最后一行不足5个字节时: // 仅会覆盖最后一行获取到的相应的字节数,比如最后一行3字节,仅覆盖p的前3个,第4、5个字节维持上次循环时的赋值 // 所以这里输出时使用了:p[:n] fmt.Println(n, string(p[:n])) }
如上执行结果:
5 clear
5 is b5 etter5 than5 clev2 erlevEOF: 0exit status 1
以上就是golang之资源释放/异常错误处理解析的详细内容,更多关于golang资源释放异常错误处理的资料请关注编程客栈(www.devze.com)其它相关文章!
精彩评论