go中可以抛出一个panic的异常,然后在defer中通过recover捕获异常,然后生成终止函数(程序)
defer介绍
defer语法介绍
1.defer函数的参数被计算在defer被声明的的时候。
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
//结果: 打印的i值为0,因为i值为0的时候,使用了defer关键字
2.defer函数的执行按照后进先出 的顺序。
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}
//结果: “3210”,(后进先出的顺序,无需解释)
3.defer函数可以读取和分配返回函数的返回值(Deferred functions may read and assign to the returning function’s named return values)。
func c() (i int) {
defer func() { i++ }()
return 1
}
//结果: 函数返回值为2
//原因:return 1会将函数返回值i赋值为1, 然后执行i++,所以返回值为2
//这里出一道结合了1,3两个用法的题。
func c() (i int) {
defer func(i int) {
i++
fmt.Println("i's value ", i)
}(i)
return 1
}
//结果:函数的返回值为1,打印内容为“i's value 1"
//原因:defer函数在定义的时候就确定参数的值
defer的执行次序
defer、return、返回值三者的执行逻辑应该是:return最先执行,return负责将结果写入返回值;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出。
1.无名返回值的情况
package main
import (
"fmt"
)
func main() {
fmt.Println("return:", a()) // 打印结果为 return: 0
}
func a() int {
var i int
defer func() {
i++
fmt.Println("defer2:", i) // 打印结果为 defer: 2
}()
defer func() {
i++
fmt.Println("defer1:", i) // 打印结果为 defer: 1
}()
return i
}
//原因:函数的返回值没有被提前声名,其值来自于其他变量的赋值,而defer中修改的也是其他变量,而非返回值本身,因此函数退出时返回值并没有被改变。
2.有名返回值的情况
package main
import (
"fmt"
)
func main() {
fmt.Println("return:", b()) // 打印结果为 return: 2
}
func b() (i int) {
defer func() {
i++
fmt.Println("defer2:", i) // 打印结果为 defer: 2
}()
defer func() {
i++
fmt.Println("defer1:", i) // 打印结果为 defer: 1
}()
return i // 或者直接 return 效果相同
}
//原因:函数的返回值被提前声名,也就意味着defer中是可以调用到真实返回值的,因此defer在return赋值返回值 i 之后,再一次地修改了 i 的值,最终函数退出后的返回值才会是defer修改过的值。
3.返回指针的情况
package main
import (
"fmt"
)
func main() {
fmt.Println("c return:", *(c())) // 打印结果为 c return: 2
}
func c() *int {
var i int
defer func() {
i++
fmt.Println("c defer2:", i) // 打印结果为 c defer: 2
}()
defer func() {
i++
fmt.Println("c defer1:", i) // 打印结果为 c defer: 1
}()
return &i
}
//原因:c()*int 的返回值是指针变量,那么在return将变量 i 的地址赋给返回值后,defer再次修改了 i 在内存中的实际值,因此函数退出时返回值虽然依旧是原来的指针地址,但是其指向的内存实际值已经被成功修改了。
panic
panic是用来表示非常严重的不可恢复的错误的。在Go语言中这是一个内置函数,接收一个interface{}类型的值作为参数。如果不使用recover,panic会导致程序直接挂掉。关键的一点是,在函数执行panic的时候,函数会首先执行defer定义的内容,在defer内容执行完之后,panic再向上传递。
recover
recover也是一个Go语言的内置函数,用于再次恢复panic的goroutine。recover必须在defer函数中使用 。当正常执行的是时候,调用recover将会返回nil并且不会有任何其他影响;如果当前的goroutine是panic,调用recover将捕捉到panic传入的值并且再次正常执行。
注意: recover之后,逻辑并不会恢复到panic那个点去,函数还是会在defer之后返回。
综合例子展示
package main
import "fmt"
func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}
结果
Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.
此处不做解释,如果不懂,返回查看前文描述的内容即可。