上一节课介绍了常见的流程控制,这一节课将介绍一个 Go 特有的流程控制语句: defer
defer 通常用于延迟调用指定的函数。例如:
func outerFunc() { defer fmt.Printf(" World!\n") fmt.Print("Hello") }
上例最终输出的结果是: "Hello World!".
这是因为:defer 会在 outerFunc 退出之前执行打印操作,因此被 defer 调用的函数也称为“延迟函数”。
defer
outerFunc
defer语句经常被用于处理成对的操作,如打开和关闭,连接和断开连接,加锁和释放锁。恰当使用 defer 能够保证资源正确释放。 以下是几个例子:
// 使用 defer 关闭 http 请求响应体的 Body func closeBody(url string) error { resp, err := http.Get(url) defer resp.Body.Close() // ... do more stuff ... return err }
// 使用 defer 关闭文件句柄 func closeFile(filename string) error{ f, err := os.Open(filename) defer f.Close() // ... do more stuff ... return err }
// 使用 defer 解锁 func BillCustomer(c *Customer) { c.mutex.Lock() defer c.mutex.Unlock() // ... do more stuff ... return }
请看以下例子,猜下输出结果是?
func printNumber() { for i := 0; i<5; i++{ defer func(){ fmt.Println(i) }() } }
最终输出的结果是 5 5 5 5 5。 这是因为 defer 所调用的函数是延迟执行的。等到执行 defer 所调用的函数时,i 已经是 5 了。 接着看下面这个例子:
5 5 5 5 5
func printNum() { for i := 0; i < 5; i++ { defer func(v int) { fmt.Println(v) }(i) } }
这个例子最终输出的是: 4 3 2 1 0。具体是什么原因留作大家思考。
4 3 2 1 0
func testDefer() (i int) { defer func() { fmt.Println(i) i = 4 }() return 2 }
以上例子,最终返回的是 4。因为return 2 执行后,变量 i 赋值为 2, 但是随后执行了 defer 函数,i 被赋值为4,所以最终返回结果为4。
return 2
i
当程序遇到致命错误导致无法继续运行时就会出发 panic , 例如:数组越界,空指针等。
panic
以下代码将会出发数组越界异常。
s := []int{1, 2, 3} for i := 0; i <= 4; i++ { fmt.Println(s[i]) }
上述例子中因为数组越界,触发了 runtime 异常,导致程序退出。在实际开发中,也可以主动调用 panic 函数达到同样效果。
func panicFunc() { panic(errors.New("this is test for panic")) }
顾名思义,recover 函数能使当前程序从 panic 中恢复。recover 能够拦截 panic 事件,使得程序不会因为意外而触发 panic 事件而完全退出。
recover 函数返回的是一个 interfac{} 类型的结果,如果捕获到了 panic 事件,该结果就为非 nil。见下例:
func panicFunc() { defer func() { if p := recover(); p != nil { fmt.Println("recover panic") } }() panic(errors.New("this is test for panic")) } func main() { fmt.Println("before panic") panicFunc() fmt.Println("after panic") }
func printNumbers(){ for i:=0; i<5; i++{ defer func(n int){ fmt.Printf(n) }(i * 2) } }
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8