defer在声明时不会当即执行,而是在defer所在的函数return后,再按照FILO(先进后出)的原则依次执行每个defer,通常用于异常处理、释放资源、清理数据、记录日志等。
每次defer语句执行时,defer修饰函数的返回值和参数取值会照常进行计算和保存,可是defer修饰的函数不会执行。等到上一级函数返回前,会按照defer的声明顺序倒序执行所有defer的函数。defer所修饰函数的任何返回值都会被丢弃。
若是一个defer所修饰函数的值为nil,则defer的函数会在函数执行时panic(异常),而不会在defer语句执行时panic。defer所修饰函数的上一级函数即便抛出异常,defer所修饰函数也会被执行的,确保资源被合法释放。
defer延迟函数使用示例以下:数组
package main import "fmt" func deferTest() { defer fmt.Println(1) defer fmt.Println(2) fmt.Println(3) for i := 100; i < 105; i++ { defer fmt.Println(i) //执行defer时进行参数计算 } } func main() { deferTest() } // output: 3 104 103 102 101 100 2 1
A、简化资源回收安全
mu.Lock() defer mu.Unlock()
defer 有必定的开销, 为了节省性能能够避免使用的defer
B、捕获panic异常
Go语言中,panic用于抛出异常,,recover用于捕获异常。
recover只能在defer语句中使用,直接调用recover是无效的。app
package main import "fmt" func deferRecover(){ defer func () { if r := recover(); r != nil { fmt.Println("recover") } }() fmt.Println("exception will be happen") panic("exception has happped.") fmt.Println("return normally") } func main() { deferRecover() }
C、修改返回值
defer能够用于在 return 后修改函数的返回值。ide
package main import "fmt" func deferReturn(a,b int)(sum int){ defer func(){ sum += 100 }() sum = a + b return sum } func main() { sum := deferReturn(1,6) fmt.Println(sum)//107 }
D、安全回收资源函数
func set(mu *sync.Mutex, arr []int, i, v int) { mu.Lock() defer mu.Unlock() arr[i] = v }
若是运行时抛出切片越界异常,能够保证mu.Unlock()被调用。性能
Go语言经过内置的错误接口提供了简单的错误处理机制。
error类型是一个接口类型,定义以下:日志
type error interface { Error() string}
Golang中引入error接口类型做为错误处理的标准模式,若是函数要返回错误,则返回值类型列表中确定包含error。code
package main import ( "fmt" "errors" ) //定义一个DivideError类型 type DivideError struct { dividee int divider int } //实现error接口 func (err *DivideError) Error() error{ strFormat := `Cannot proceed, the divider is zero. dividee: %d divider: 0` return errors.New(fmt.Sprintf(strFormat, err.dividee)) } //定义除法运算 func divide(vardividee int, vardivider int)(result int, errmsg error){ if vardivider == 0{ divideErr := DivideError{ dividee:vardividee, divider:vardivider, } errmsg = divideErr.Error() return 0,errmsg }else{ return vardividee/vardivider,nil } } func main() { //正常状况 if result, err := divide(100, 10); err != nil{ fmt.Println(err) }else{ fmt.Println("100/10 = ", result) } //当被除数为零的时候会返回错误信息 if _, errorMsg := divide(100, 0); errorMsg != nil{ fmt.Println(errorMsg) } }
Go使用panic()函数抛出异常,在defer语句中调用recover()函数捕获异常。orm
func panic(interface{})//接受任意类型参数 无返回值 func recover() interface{}//能够返回任意类型 无参数
panic()是一个内置函数,能够中断原有的控制流程,进入一个panic流程中。当函数F调用panic,函数F的执行被中断,但F中的延迟函数(必须是在panic前的已加载的defer)会正常执行,而后F函数逐层向上返回,直到发生panic的goroutine中全部调用的函数返回,此时程序退出。异常能够直接调用panic产生,也能够由运行时错误产生,例如访问越界的数组。
recover()是一个内置函数,可让进入panic流程中的goroutine恢复过来。recover仅在延迟函数中有效。在正常的执行过程当中,调用recover会返回nil,而且没有其它任何效果。若是当前的goroutine陷入panic,调用recover能够捕获到panic的输入值,而且恢复正常的执行。
通常状况下,recover()应该在一个使用defer关键字的函数中执行以有效截取错误处理流程。若是没有在发生异常的goroutine中明确调用恢复过程(使用recover关键字),会致使goroutine所属的进程打印异常信息后直接退出。接口
package main import ( "errors" "fmt" ) //定义一个DivideError类型 type DivideError struct { dividee int divider int } //实现error接口 func (err *DivideError) Error() error{ strFormat := `Cannot proceed, the divider is zero. dividee: %d divider: 0` return errors.New(fmt.Sprintf(strFormat, err.dividee)) } //定义除法运算 func divide(dividee int, divider int)(result int){ defer func() { if r := recover();r != nil{ divideErr := DivideError{ dividee:dividee, divider:divider, } fmt.Println(divideErr.Error()) } }() result = dividee/divider return result } func main() { a := divide(100,0) fmt.Println(a) }