1、什么是错误
- 错误:指的是程序中预期会发生的结果,预料之中,错误是业务的一部分
- 异常:不该出现问题的地方出现了问题,预料之外,异常不是业务的一部分
在实际项目中,我们希望通过程序的错误信息快速定位问题,
Go语言没有提供像java,c# 语言中的try…catch异常捕获处理方式,而是通过函数返回值逐层往上抛。
为什么这样设计?
这种设计,鼓励工程师在代码中显示的检查错误,而非忽略错误
- 好处是避免漏掉本应处理的错误
- 弊端是让代码繁琐
go语言中用内置的error 类型表示,就像其他的int、float64一样,错误值可以存储在变量中,从函数中返回
例子代码:
import (
"fmt"
"os"
)
func main() {
// 打开一个文件
file,err := os.Open("aa.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(file.Name())
}
执行结果:
open aa.txt: no such file or directory
点进os.Open的源码
2、自定义错误
创建自己的error错误信息
// 创建一个自己的error错误信息,通过errors创建,返回的是内置的error类型指针对象
errors.New("我是一个错误信息")
// 也可以通过fmt创建
fmt.Errorf("我是一个错误信息,%d\n",500)
看源码
import (
"errors"
"fmt"
)
func main() {
errinfo := errors.New("我是一个错误信息")
// 打印变量值
fmt.Println(errinfo)
fmt.Println(errinfo.Error())
// 打印变量的数据类型
fmt.Printf("%T\n",errinfo) // *errors.errorString 指针变量
err:=setAge(-1)
if err !=nil {
fmt.Println(err)
}
}
func setAge(a int) error{
if a < 0 {
return errors.New("年龄输入不合法")
}
fmt.Println(a)
return nil
}
执行结果:
我是一个错误信息
我是一个错误信息
*errors.errorString
年龄输入不合法
fmt.Errorf()
源码
errinfo2 := fmt.Errorf("我一个错误信息%d",500)
fmt.Println(errinfo2)
fmt.Printf("%T\n",errinfo2)
执行结果:
我一个错误信息500
*errors.errorString
error接口的实现类有很多,通过接口断言判断具体是什么类型的错误,然后进行处理
v,ok := err.(错误类型)
3、错误类型
error是一个接口类型
type error interface {
Error() string
}
func main() {
// 返回这个网址的服务地址
addr,err := net.LookupHost("www.baidu.com")
fmt.Println(err)
v,ok := err.(*net.DNSError)
if ok {
if v.IsTimeout {
fmt.Println("超时")
}else if v.Temporary(){
fmt.Println("临时错误")
}else {
fmt.Println("其他错误")
}
}
fmt.Println(addr)
}
执行结果
<nil>
[182.61.200.7 182.61.200.6]
自定义错误类型
例子:
type MyDiyError struct {
msg string
code int
}
// 实现error 接口
func (e MyDiyError) Error() string {
return fmt.Sprint("错误信息:",e.msg,"状态码:",e.code)
// 返回格式化string
return fmt.Sprintf("错误信息: %s,错误代码:%d",e.msg,e.code)
}
func test1(i int) (int,error) {
if i!=0 {
return i,&MyDiyError{"非零数据",500}
}
return i,nil
}
func main() {
i,err := test1(1)
if err !=nil {
fmt.Println(err)
myerr,ok :=err.(*MyDiyError) // 指针变量
if ok {
fmt.Println(myerr.msg)
fmt.Println(myerr.code)
fmt.Println(myerr.Error())
}
}
fmt.Println(i)
}
执行结果:
错误信息:非零数据状态码:500
非零数据
500
1
场景举例:购物下单
- 查询商品信息
- 查询库存(FindxxxError: msg, code ,)
- 查询物流
4、异常
panic
- 用来抛出异常
如果函数中触发了panic语句,会终止其后要执行的代码。在panic所在函数内如果存在要执行的defer函数(延迟执行),则按照defer书写顺序的逆序执行
func main() {
test(1)
}
func test(num int) {
if num == 1 {
panic("出现异常---panic")
}
}
抛出异常,终止程序执行
panic: 出现异常---panic
例子
func main() {
defer fmt.Println("main ---1")
defer fmt.Println("main ---2")
fmt.Println("main---3")
test(1)
fmt.Printf("main ---4")
defer fmt.Println("main--5")
}
func test(num int) {
defer fmt.Println("test ---1")
defer fmt.Println("test ---2")
fmt.Println("test---3")
if num == 1 {
panic("出现异常---panic")
}
fmt.Println("test---4")
}
执行结果:
main---3
test---3
test ---2
test ---1
main ---2
main ---1
- 触发panic 程序恐慌
- 逆序执行test函数中的panic前的defer语句
- 逆序执行main函数中test前的defer语句
- 抛出异常
recover
- 用户恢复异常
recover的作用是捕获panic的信息,从而恢复正常代码执行
recover必须配合defer使用
recover没有传入参数,但是有返回值,返回值就是panic传递的值
func main() {
defer fmt.Println("main ---1")
defer fmt.Println("main ---2")
fmt.Println("main---3")
test(1)
fmt.Println("main ---4")
defer fmt.Println("main---5")
}
func test(num int) {
// 匿名函数使用recover接收panic
defer func () {
msg := recover() // 获取panic抛出的异常
if msg !=nil {
fmt.Println("msg:",msg,"---程序恢复执行")
}
}()
defer fmt.Println("test ---1")
defer fmt.Println("test ---2")
fmt.Println("test---3")
if num == 1 {
panic("出现异常---panic")
}
fmt.Println("test---4")
}
执行结果
main---3
test---3
test ---2
test ---1
msg: 出现异常---panic ---程序恢复执行
main ---4
main ---5
main ---2
main ---1
- 触发panic 程序恐慌
- 逆序执行test函数中的panic前的defer语句
- 执行第三条defer时,recover恢复恐慌并输出信息
- 返回外部函数,程序继续执行,就像try catch