前言
提示:这里可以添加本文要记录的大概内容:
Go 语言在运行当中会出现panic ,为了解决和避免该问题学习defer,panic, recover 关键字
一、关键字含义
defer /recover 实现异常捕获和处理
defer 主要是指定一个延迟调用函数;
recover 发生异常的时候,可以去恢复我们的一个协程,并且拿到一个导致协程中断的消息。
应用场景
- 资源释放: 文件操作,打开文件,判断没有错误的情况下,立马去声明一个defer 调用(file close)关闭; 网络连接,tcp 连接建立连接成功之后,就可以调用defer 去关闭;
- 异常捕获和处理: 方法bi’r结束(异常结束和正常结束),都会触发defer 调用 ;
defer
- defer 关键字用来声明一个延迟调用函数,该函数可以是匿名函数也可以是具名函数
- defer 延迟函数执行时间(位置),方法return 之后,返回参数到调用方法之前; A调用B, defer 在B方法return 之后,但是在B方法值返回之前
- defer 延迟函数可以在方法返回之后改变改变函数的返回值;不是任何值都可改变,若依值类型返回不会受影响。依指针全局变量可以改变;
- 在方法结束(正常退出,异常结束)都会去调用defer声明的延迟函数,可以有效避免因异常导致的资源无法释放的问题;
- 可以指定多个defer延迟函数,多个延迟函数执行顺序为后进先出;
- defer 通常用于资源释放,异常捕获等场景。例如: 关闭连接,关闭文件等;
- defer与recover 配合可以实现异常捕获与处理逻辑;
- 不建议在for 循环中使用defer;
recover
- Go 语言内置函数,可以让进入宕机流程中的goroutine 恢复过来;比如A方法中五个步骤,在第三个触发了异常触发了defer 函数,那么recover 恢复后第三个步骤之后的步骤是不会执行。恢复协程使应用比较稳定,并不意味之按照之前的流程走。
- recover 仅在延迟函数,defer 中有效, 在正常的执行过程中,调用recover 会返回nil并且没有其他任何效果;
- 如果当前的goroutine 出现panic(其它语言说的异常),调用recover可以捕获到panic的输入值,并且恢复正常执行;
panic
- Go 语言的一种异常机制;
- 可以通过panic 函数主动抛出异常
二、实例
实例讲解上述几种情况
代码如下(示例):创建dir; main.go case/defer.go
// case/defer.go package _case import ( "fmt" "io" "log" "os" ) // defer 关键字用来声明一个延迟调用函数 // 该函数可以是匿名函数也可以是具名函数 // defer 延迟函数的执行顺序为后进先出 func DeferCase1() { fmt.Println("开始执行DeferCase1") defer func() { fmt.Println("调用了匿名函数1") }() defer f1() defer func() { fmt.Println("调用了匿名函数2") }() fmt.Println("DeferCase1执行结束") } // 参数预计算 func DeferCase2() { i := 1 defer func(j int) { fmt.Println("defer J:", j) }(i + 1) // 闭包 defer func() { fmt.Println("defer j:", i) }() i = 99 fmt.Println("i:", i) } // 返回值 // defer 函数执行在return 之后 func DeferCase3() { i, j := f2() fmt.Printf("j:%d,j:%d,g:%d", i, *j, g) } func f1() { fmt.Println("调用了具名函数1") } var g = 100 func f2() (int, *int) { defer func() { g = 200 }() fmt.Println("f2 g:", g) return g, &g } func FileReadCase() { file, err := os.Open("README.md") if err != nil { log.Fatal(err) } //通过defer 调用资源释放方法 defer func() { file.Close() fmt.Println("释放文件资源") }() buf := make([]byte, 1024) for { n, err := file.Read(buf) if err != nil && err != io.EOF { log.Fatal(err) } if n == 0 { break } fmt.Println(buf[:n]) } } func ExceptionCase() { defer func() { // 捕获异常, 恢复协程 err := recover() // 异常处理 if err != nil { fmt.Println("异常处理 defer recover", err) } }() fmt.Println("开始执行ExceptionCase方法") panic("ExceptionCase抛出异常") fmt.Println("ExceptionCase方法执行结束") }
// main.go package main import _case "defer_recover_panic/case" func main() { //_case.DeferCase1() //_case.DeferCase2() //_case.DeferCase3() //_case.ExceptionCase() _case.FileReadCase() }
总结
略