异常处理
什么是错误error
错误指的是可能出现问题的地方出现了问题,比如打开一个文件时失败,这种情况在人们的意料之中
和错误类似的还有一个异常,指的是不应该出现问题的地方出现了问题。比如引用了空指针,这种情况在人们的意料之外,可见,错误是业务过程的一部分,而异常不是
package main
import (
"fmt"
"os"
)
func main() {
//打开一个文件
file, err := os.Open("aaa.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(file.Name())
}
在实际工程项目中,我们希望通过程序的错误信息快速定位问题,但是又不喜欢错误处理代码写的冗余而又哆嗦。
Go语言没有提供像java等语言中的try…catch异常处理方式,而是通过函数返回值逐层往上抛
这种设计,鼓励工程师在代码中显式的检查错误,而非忽略错误
好处就是避免漏掉本应处理的错误。但是带来了一个弊端,让代码繁琐
Go中的错误也是一种类型,错误用内置的error类型表示,就像其他类型的,如int,float64.
错误值可以存储在变量中,从函数中返回等等。
创建自己的error错误信息
package main
import (
"errors"
"fmt"
)
func main() {
errinfo := errors.New("我是一个错误信息")
fmt.Println(errinfo)
fmt.Printf("%T\n", errinfo)
err := setAge(-1)
if err != nil {
fmt.Println(err)
}
errinfo2 := fmt.Errorf("我是一个错误%d", 500)
fmt.Println(errinfo2)
fmt.Printf("%T\n", errinfo2)
}
func setAge(age int) error {
if age < 0 {
return errors.New("年龄输入不合法")
}
fmt.Println(age)
return nil
}
错误类型
error类型是一个接口类型
package main
import (
"fmt"
"net"
)
type error interface {
Error() string
}
func main() {
addrs, err := net.LookupHost("www.baidu.com")
fmt.Println(err)
dnsError, ok := err.(*net.DNSError)
if ok {
if dnsError.Timeout() {
fmt.Println("超时")
} else if dnsError.Temporary() {
fmt.Println("临时错误")
} else {
fmt.Println("其他错误")
}
}
fmt.Println(addrs)
}
通过断言,来判断具体的错误类型,然后进行对应的错误处理
自定义error
自定义错误,需要实现error()接口中的Error()方法
package main
import "fmt"
type MyDiyError struct {
msg string
code int
}
func (e MyDiyError) Error() string {
return fmt.Sprint("错误信息:", 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(i)
}
panic和recover
panic
如果函数F中书写并出发了panic语句,会终止其后要执行的代码。在panic所在函数F内如果存在要执行的defer函数列表,则按照defer书写顺序的逆序执行
package main
import "fmt"
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) {
defer fmt.Println("test-----1")
defer fmt.Println("test-----2")
fmt.Println("test-----3")
if num == 1 {
panic("出现一次----panic")
}
fmt.Println("test----4")
defer fmt.Println("test----5")
}
- 触发panic
- 逆序执行test函数中的panic前的2条defer语句
- 返回外部函数(main函数)
- 逆序执行main函数中的test函数前的2条defer语句
- 抛出异常
由于panic抛出异常,可以看到test函数panic语句后面的语句都不被执行;返回外部函数后,test函数后的语句也都不被执行
recover
- recover的作用是捕获panic,从而恢复正常代码执行
- recover必须配合defer使用
- recover没有传入参数,但是有返回值,返回值就是panic传递的值
package main
import "fmt"
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) {
defer func() {
msg := recover()
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")
defer fmt.Println("test----5")
}
- 触发panic
- 逆序执行test函数中的panic前的3条defer语句
- 执行到第三条defer时,recover恢复恐慌并输出信息
- 返回外部函数,程序继续执行
由于recover恢复恐慌,所以程序就不会因为panic而退出执行,外部函数还能正常执行下去