Go-defer,panic,recover
defer
语法:
defer function_name()
简单来讲,在defer所在函数执行完所有的代码之后,会自动执行defer的这个函数。
示例一(基本功能)
package main
import "fmt"
/*
D:examples>go run helloworld.go
first
second
D:examples>
*/
func main() {
defer second()
first()
}
func first() {
fmt.Println("first")
}
func second() {
fmt.Println("second")
}
示例二(函数局部性)
package main
import "fmt"
/*
D:examples>go run helloworld.go
first
third
first
second
D:examples>
*/
func main() {
defer second()
first()
third()
}
func first() {
fmt.Println("first")
}
func second() {
fmt.Println("second")
}
func third() {
defer first()
fmt.Println("third")
}
示例三(栈特性)
package main
import "fmt"
/*
D:examples>go run helloworld.go
first
third
second
D:examples>
*/
func main() {
defer second()
defer third()
first()
}
func first() {
fmt.Println("first")
}
func second() {
fmt.Println("second")
}
func third() {
fmt.Println("third")
}
使用场景
主要用于资源需要释放的场景。比如打开一个文件,最后总是要关闭的。而在打开和关闭之间,会有诸多的处理,可能会有诸多的if-else、根据不同的情况需要提前返回。在传统语言中,return之前都需要一一调用close()。
而Go的defer就将事情变得简单了,open()之后,直接就用defer“注册”一个close()。伪代码:
f, = os.open(filename)
defer f.close()
do_something()
if (condition_a) {return}
do_something_again()
if (condition_b) {return}
do_further_things()
panic & recover
先给出https://golang.org/pkg/builtin/上的函数说明。
panic
func panic(v interface{})
The panic built-in function stops normal execution of the current goroutine. When a function F calls panic, normal execution of F stops immediately. Any functions whose execution was deferred by F are run in the usual way, and then F returns to its caller. To the caller G, the invocation of F then behaves like a call to panic, terminating G’s execution and running any deferred functions. This continues until all functions in the executing goroutine have stopped, in reverse order. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. This termination sequence is called panicking and can be controlled by the built-in function recover.
recover
func recover() interface{}
The recover built-in function allows a program to manage behavior of a panicking goroutine. Executing a call to recover inside a deferred function (but not any function called by it) stops the panicking sequence by restoring normal execution and retrieves the error value passed to the call of panic. If recover is called outside the deferred function it will not stop a panicking sequence. In this case, or when the goroutine is not panicking, or if the argument supplied to panic was nil, recover returns nil. Thus the return value from recover reports whether the goroutine is panicking.
要点
- panic相当于一个运行时异常
- 遇到panic的时候,会停止当前函数剩下来的语句,但在退出该函数之前,会执行defer的语句
- 依据函数调用层次,panic依次终止每个函数,直至main()。
panic示例
package main
import "fmt"
/*
D:examples>go run helloworld.go
f.1
g.1
h.1
h.defer()
g.defer()
panic: panic in h()
goroutine 1 [running]:
panic(0x495360, 0xc04203a230)
C:/Go/src/runtime/panic.go:500 +0x1af
main.h()
D:/examples/helloworld.go:54 +0x12b
main.g()
D:/examples/helloworld.go:45 +0xee
main.f()
D:/examples/helloworld.go:38 +0xab
main.main()
D:/examples/helloworld.go:29 +0x1b
exit status 2
D:examples>
*/
func main() {
f() // Line Number: 29
}
func final_print(msg string) {
fmt.Println(msg)
}
func f() {
fmt.Println("f.1")
g() // Line Number: 38
fmt.Println("f.2")
}
func g() {
defer final_print("g.defer()")
fmt.Println("g.1")
h() // Line Number: 45
fmt.Println("g.2")
}
func h() {
defer final_print("h.defer()")
fmt.Println("h.1")
panic("panic in h()") // Line Number: 52
fmt.Println("h.2")
}
panic & defer & recover
recover相当于try-catch的catch部分,使得panic不再传递。而defer相当于try-catch-final的final部分。
package main
import "fmt"
/*
D:examples>go run helloworld.go
f.1
g.1
h.1
h.defer()
g.defer()
panic in h()
f.2
D:examples>
*/
func main() {
f()
}
func final_print(msg string) {
fmt.Println(msg)
}
func f() {
fmt.Println("f.1")
g()
fmt.Println("f.2")
}
func g() {
defer func() {
str := recover()
fmt.Println(str)
}()
defer final_print("g.defer()")
fmt.Println("g.1")
h()
fmt.Println("g.2")
}
func h() {
defer final_print("h.defer()")
fmt.Println("h.1")
panic("panic in h()")
fmt.Println("h.2")
}
获取数组元素
接下来再给一个例子,获取数组元素,处理数组访问越界的问题。
package main
import "fmt"
/*
D:examples>go run helloworld.go
a[0]=1[true]
a[1]=2[true]
a[2]=3[true]
a[3]=4[true]
a[4]=5[true]
runtime error: index out of range [set to default value -1]
a[5]=-1[false]
runtime error: index out of range [set to default value -1]
a[6]=-1[false]
runtime error: index out of range [set to default value -1]
a[7]=-1[false]
runtime error: index out of range [set to default value -1]
a[8]=-1[false]
runtime error: index out of range [set to default value -1]
a[9]=-1[false]
D:examples>
*/
func main() {
a := [5]int {1,2,3,4,5}
for i := 0; i < 10; i++ {
item, ok := get(i, a)
fmt.Printf("a[%d]=%d[%v]n", i, item, ok)
}
}
func get(i int, a [5]int) (ret int, ok bool) {
ok = true
defer func() {
err := recover()
if err != nil {
fmt.Println(err, "[set to default value -1]")
ret = -1
ok = false
}
}()
ret = a[i]
return
}
- Git面试常见问题
- 剑指offer代码解析——面试题17合并两个排序的链表
- MySQL优化方案(一)优化SQL脚本与索引
- 从0学习MySQL系列(二)安装篇
- 从0学习MySQL系列(三)概念篇
- SQL Server基础SQL脚本之主外键约束
- SQL Server基础SQL脚本之Group By
- SQL Server基础SQL脚本之内外连接、交叉连接;函数、子查询
- MySQL 面试选择题15道(单选)
- SQL Server基础SQL脚本之分区表、分区方案
- SQL Server基础SQL脚本之创建架构、排序
- 枚举算法(Enumeration algorithm)实例一
- 剑指offer代码解析——面试题16反转单链表
- QMainWindow 和 QWidget 设置layout
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【剑指Offer】树的子结构
- 【剑指Offer】合并两个排序的链表
- Python实战之特定文本提取,挑战高效办公的第一步
- 【剑指Offer】调整数组顺序使奇数位于偶数前面
- 【剑指Offer】链表中倒数第 k 个节点
- 【剑指Offer】调整数组顺序使奇数位于偶数前面
- 【剑指Offer】复杂链表的复制
- 【剑指Offer】二叉树中和为某一值的路径
- 【剑指Offer】二叉搜索树的后序遍历序列
- 【剑指Offer】Ⅲ. 从上到下打印二叉树
- scRepertoire||单细胞免疫组库分析:R语言应用(二)
- 【剑指Offer】Ⅱ. 从上到下打印二叉树
- 【剑指Offer】I. 从上到下打印二叉树
- C#还能这么玩?“诺基亚大屏独显计算器”来咯!
- 【剑指Offer】栈的压入、弹出序列