总有同学会认为错误和异常是一个概念,其实不然那么错误和异常有什么区别呢?
错误是意料之中,异常是意料之外
错误
1.error
go语言内置的错误类型 error 本质上是接口类型,我们如果需要自定义错误类型,那么也需要将其实现为 error 接口类型,这样调用总是可以通过 Error() 获取到具体的错误信息而不用关心错误的具体类型。标准库的 fmt.Errorf
和 errors.New
可以方便的创建 error 类型的变量。
type error interface {
Error() string
}
golang 的多返回值语法糖避免了这种方式带来的不便,错误值一般作为返回值列表的最后一个,其他返回值是成功执行时需要返回的信息。为了避免错误处理时过深的代码缩进:
if err != nil {
// error handling} else { // normal code
}
推荐在发生错误时立即返回:
if err != nil {
// error handling return // or continue, etc.
}// normal code
预定义错误值
预定义错误值,当函数需要返回错误时,每次都可以调用 errors.New()
来返回error 的对象:
func Cacl()error {
if one1 {
return errors.New("no space left on the host")
}else {
return errors.New("maybe permission denied")
}
}
上面的例子有个问题是不太方便对定义的错误值进行判断,最好的做法是可以先定义一个全局的错误值:
var ErrNoSpace = errors.New("no space left on the host")
var ErrPermissionDenied = errors.New("maybe permission denied")
func Cacl() error {
if one1 {
return ErrNoSpace
} else {
return ErrPermissionDenied
}
}
这样十分利于错误值的判断:
if err == ErrNoSpace {
//return ...
}
自定义错误类型
HTTP 表示客户端的错误状态码有很多。如果为每种状态码都需要先定义相应的错误值,代码将会变得十分繁琐:
var ErrBadRequest = errors.New("status code 400: bad request")
var ErrUnauthorized = errors.New("status code 401: unauthorized")
// ...
这种场景下最佳的最法是自定义一种错误类型,并且至少实现 Error()
方法:
type HTTPError struct {
Code int Description string
}
func (h *HTTPError) Error() string {
return fmt.Sprintf("status code %d: %s", h.Code, h.Description)
}
异常处理
1.defer
defer是什么?
go编写程序时常用defer来充当延迟调用机制的角色,编写在defer后面的语句只能在当前语句执行完成以后才能继续执行,通常也用来进行资源的释放。
defer与栈的结构类似,遵循着先进后出的原则。
补充:defer设计先进后出机制的原因?
防止后申请的资源对依赖前置申请的资源,需要先释放后申请的资源。
defer示例
package main
import ( "fmt")
func main() {
defer func() {
fmt.Println("2")
}()
fmt.Println("1")
}
输出结果
12Process finished with exit code 0
当有多个 defer 行为被注册时,它们会以后进先出去执行, 相当于开辟了一个延时调用栈
也基于defer
语句可以延迟调用的特性,故而defer
可以十分方便的处理资源释放问题。例如:文件的关闭、解锁和时间记录等系列操作。
defer并发延迟解锁
var (
// 初始化map(key为string类型,value为int类型)
map1 = make(map[string]int)
// 使用sync包中提供的Mutex类型来实现互斥锁
m sync.Mutex
)
// 定义取值函数
func ReadValue(k1 string) int {
// 加锁
m.Lock()
// 根据map1中的key取出value
v1 := map1[key]
// 解锁
m.Unlock()
// 返回value
return v1
}
使用 defer 语句对上面的语句进行简化,参考下面的代码。
func ReadValue(k1 string) int {
m.Lock()
// defer后面的语句不会被立刻调用执行, 而是会延迟到该函数结束的时候才会被调用
defer m.Unlock()
return map1[key]
}
2.panic
函数中遇到panic语句,会立即终止当前函数的执行
例:
package main
import "fmt"
func main() {
fmt.Println("start main")
a()
fmt.Println("end main")
}
func a() {
fmt.Println("start a")
panic("panic in a")
fmt.Println("end a")
}
结果
start a
panic: panic in a
goroutine 1 [running]:
main.a()
D:/tools/goland/src/book/test123.go:14 +0xa5
main.main()
D:/tools/goland/src/book/test123.go:8 +0x8a
Process finished with exit code 2
3.recover
使用recover()去捕获panic()并恢复执行。recover()用于捕捉panic()错误,并返回这个错误信息。
package main
import "fmt"
func main() {
fmt.Println("start main")
a()
fmt.Println("end main")
}
func a() {
fmt.Println("start a")
defer func() {
if str := recover(); str != nil {
fmt.Println(str)
}
}()
panic("panic in a")
fmt.Println("end a")
}
\
本文正在参加技术专题18期-聊聊Go语言框架\
\