0
点赞
收藏
分享

微信扫一扫

GoLang之defer、panic、recover

未定义变量 2022-03-12 阅读 58

文章目录

1.defer关键字

2.defer特性、用途

image-20220123202809611

3.painc内置函数

func panic(v interface{})

image-20220125083345829

4.recover内置函数

func recover() interface{}

5.defer在ReadFile函数内关闭句柄

//比如我们看 Go 内置的 io/ioutil 包提供的读取文件方法 ReadFile 实现源码,其中就有 defer 语句的使用:
func ReadFile(filename string) ([]byte, error) {
    f, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer f.Close()

    var n int64 = bytes.MinRead

    if fi, err := f.Stat(); err == nil {
        if size := fi.Size() + bytes.MinRead; size > n {
            n = size
        }
    }
    return readAll(f, n)
}

6.defer在闭包中的应用(捕获变量被初始化赋值且修改)

image-20220123203132149

7.先return再defer

image-20220125091718500

image-20220123203541168

image-20220125091436237

image-20220123203621904

8.defer在遍历执行结构体方法中的应用

image-20220123204337077
image-20220123204452862

9.defer在遍历执行函数中的应用

image-20220123204420793

10.defer遍历切片输出索引

image-20220123203029302

image-20220123202950124

11.自发panic

//底层抛出 panic
package main

import "fmt"

func main() {
	var i = 1
	var j = 0
	var k = i / j

	fmt.Printf("%d / %d = %d\n", i, j, k)
}
/*                                        
panic: runtime error: integer divide by zero    
goroutine 1 [running]:                          
main.main()                                     
        D:/all project/go/demo/main/1.go:8 +0x11
*/

12.使用defer自定义错误输出在panic之前

//在这段代码中,我们定义了两个 defer 语句,并且是在函数最顶部,以确保异常情况下也能执行。
/*在函数正常执行的情况下,这两个 defer 语句会在最后一条打印语句执行完成后先执行第二条 defer 语句,再执行第一条 defer 语句:
*/
package main

import "fmt"

func printError()  {
    fmt.Println("兜底执行")
}

func main()  {
    defer printError()
    defer func() {
        fmt.Println("除数不能是0!")
    }()

    var i = 1
    var j = 1
    var k = i / j

    fmt.Printf("%d / %d = %d\n", i, j, k)
}
/*输出以下三行:
1 / 1 = 1
除数不能是0!
兜底执行    
*/
//底层抛出 panic
//而如果我们把 j 的值设置为 0,则函数会抛出 panic:
/*表示除数不能为零。这个时候,由于 defer 语句定义在抛出 panic 代码的前面,所以依然会被执行,底层的逻辑是在执行 var k = i / j 这条语句时,遇到除数为 0,则抛出 panic,然后立即中断当前函数 main 的执行(后续其他语句都不再执行),并按照先进后出顺序依次执行已经在当前函数中声明过的 defer 语句,最后打印出 panic 日志及错误信息*/
package main

import "fmt"

func printError() {
	fmt.Println("兜底执行")
}

func main() {
	defer printError()
	defer func() {
		fmt.Println("除数不能是0!")
	}()

	var i = 1
	var j = 0
	var k = i / j

	fmt.Printf("%d / %d = %d\n", i, j, k)
}
/*
除数不能是0!
兜底执行                                         
panic: runtime error: integer divide by zero    
goroutine 1 [running]:                           
main.main()                                      
        D:/all project/go/demo/main/1.go:20 +0x46
*/

13.使用panic

image-20220125083817468

15.使用recover

image-20220125085933322

image-20220125085702011

image-20220125085712441

/*没有通过 recover() 函数捕获 panic 的话,程序会直接崩溃退出,并打印错误和堆栈信息,但现在我们在 divide() 函数的 defer 语句中通过 recover() 函数捕获了 panic,并打印捕获到的错误信息,这个时候,程序会退出 divide() 函数而不是整个应用,继续执行 main() 函数中的后续代码,即恢复后续其他代码的执行:
*/
package main

import (
    "fmt"
)

func divide() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Printf("Runtime panic caught: %v\n", err)
        }
    }()

    var i = 1
    var j = 0
    k := i / j
    fmt.Printf("%d / %d = %d\n", i, j, k)
}

func main() {
    divide()
    fmt.Println("divide 方法调用完毕,回到 main 函数")
}
/*输出:
Runtime panic caught: runtime error: integer divide by zero
divide 方法调用完毕,回到 main 函数
*/
/*如果在代码执行过程中没有抛出 panic,比如我们把 divide() 函数中的 j 值改为 1,则代码会正常执行到函数末尾,然后调用 defer 语句声明的匿名函数,此时 recover() 函数返回值为 nil,不会执行 if 分支代码,然后退出 divide() 函数回到 main() 函数执行后续代码*/
package main

import (
	"fmt"
)

func divide() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Printf("Runtime panic caught: %v\n", err)
		}
	}()

	var i = 1
	var j = 1
	k := i / j
	fmt.Printf("%d / %d = %d\n", i, j, k)
}

func main() {
	divide()
	fmt.Println("divide 方法调用完毕,回到 main 函数")
}
/*输出:
1 / 1 = 1
divide 方法调用完毕,回到 main 函数
*/
举报

相关推荐

0 条评论