在实际的项目中,对于异常的最佳实践不少,在使用不一样的语言开发不一样类型的程序时有不一样的建议。Go语言中没有使用try...catch相似的异常处理机制,而是提供了panic和recover函数来处理所谓的运行时异常,也就是所谓的错误处理机制。配合defer语句和error接口开发者能够很是灵活地处理运行时的错误和异常。bash
代码中随便使用panic显然不是一个好方式,咱们知道在风险控制中,有所谓已知的未知和未知的未知,在Go程序设计中,对于前者咱们能够经过预先开发的代码分支来处理,对于后者就比较棘手了:ide
咱们应该认识到,panic函数是咱们和计算机都不但愿看到的,应该在设计开发的时候充分考虑使用场景可能出现的状况,处理好已知的未知,在肯定须要的地方使用panic机制。函数
defer关键字用来标记最后执行的Go语句,通常用在资源释放、关闭链接等操做,会在函数关闭前调用。多个defer的定义与执行相似于栈的操做:先进后出,最早定义的最后执行。ui
package main
import (
"fmt"
)
func testA() int {
x := 5
defer func() {
x++
fmt.Printf("testA defer func-1[x=>%d]\n", x)
}()
defer func() {
x++
fmt.Printf("testA defer funn-2[x=>%d]\n", x)
}()
return x
}
func testB() (x int) {
x = 5
defer func() {
fmt.Println("testB defer func")
}()
x++
return
}
func main() {
a := testA()
b := testB()
fmt.Printf("testA return %d\n", a)
fmt.Printf("testB return %d\n", b)
}
复制代码
执行后输出结果:编码
testA defer funn-2[x=>6]
testA defer func-1[x=>7]
testB defer func
testA return 5
testB return 6
复制代码
分析如上代码,return xxx
语句并非一条原子指令,在其执行的时候语句会分解成返回变量=xxx
和return
,最后执行return
。以前说defer
语句是在函数关闭的时候调用,确切的说是在执行return
语句的时候调用,注意是return
不是return xxx
。在同一个函数里的defer会按照栈的先进后出原则执行。spa
Go语言经过支持多返回值,让在运行时返回详细的错误信息给调用者变得很是方便。咱们能够在编码中经过实现error接口类型来生成错误信息,error接口的定义以下:设计
type error interface {
Error() string
}
复制代码
经过下面的例子来看一下error接口的使用:code
package main
import (
"fmt"
)
type error interface {
Error() string
}
// DivisionError 类型定义
type DivisionError struct {
dividend int
divider int
}
// Error 方法实现
func (de *DivisionError) Error() string {
strFormat := ` Cannot proceed, the divider is zero. dividend: %d divider: 0`
return fmt.Sprintf(strFormat, de.dividend)
}
// Divide 函数定义
func Divide(varDividend int, varDivider int) (result int, errorMsg string) {
if varDivider == 0 {
dData := DivisionError{
dividend: varDividend,
divider: varDivider,
}
errorMsg = dData.Error()
} else {
result = varDividend / varDivider
}
return
}
func main() {
result, err := Divide(100, 10)
if err == "" {
fmt.Printf("100/10=%d\n", result)
}
_, err = Divide(100, 0)
if err != "" {
fmt.Printf("errorMsg is: %s\n", err)
}
}
复制代码
运行后能够看到以下输出:orm
100/10=10
errorMsg is:
Cannot proceed, the divider is zero.
dividend: 100
divider: 0
复制代码
panic和recover是两个内置函数,panic函数用于抛出异常,recover函数用于捕获panic函数的参数信息。recover函数只有在defer语句调用的函数中直接调用时才能生效,若是goroutine没有panic,那recover函数会返回nil。对象
package main
import (
"fmt"
)
// SimplePanicRecover panic/recover简单的例子
func SimplePanicRecover() {
defer func() {
if err := recover(); err != nil {
fmt.Println("Panic info is:", err)
}
}()
panic("SimplePanicRecover panic!")
}
// MultiPanicRecover 多个panic/recover
// 当defer中也调用了panic函数时,最后被调用的panic函数的参数会被后面的recover函数获取到
// 一个函数中能够定义多个defer函数,按照FILO的规则执行
func MultiPanicRecover() {
defer func() {
if err := recover(); err != nil {
fmt.Println("Panic info is:", err)
}
}()
defer func() {
panic("MultiPanicRecover defer inner panic!")
}()
defer func() {
if err := recover(); err != nil {
fmt.Println("Panic info is:", err)
}
}()
panic("MultiPanicRecover panic!")
}
// NoPanicButHasRecover 只有panic没有recover
// 若是函数没有panic函数,调用recover函数不会获取到任何信息,也不会影响当前进程
func NoPanicButHasRecover() {
if err := recover(); err != nil {
fmt.Println("NoPanicButHasRecover Panic info is:", err)
} else {
fmt.Println("NoPanicButHasRecover Panic info is:", err)
}
}
func main() {
SimplePanicRecover()
MultiPanicRecover()
NoPanicButHasRecover()
}
复制代码
运行后能够看到以下输出:
Panic info is: SimplePanicRecover panic!
Panic info is: MultiPanicRecover panic!
Panic info is: MultiPanicRecover defer inner panic!
NoPanicButHasRecover Panic info is: <nil>
复制代码