Golang 异常处理
1. 向上抛
func Div(a, b int) (res int, err error) {
if b == 0 {
err = errors.New("除数为0")
return res, err
}
res = a / b
return res, nil
}
func server() (res int, err error) {
res, err = Div(1, 0)
if err != nil {
// 把错误向上抛出
return
}
return
}
func ErrTest() {
server()
}
2. 中断程序
遇到错误直接停止程序
用法 : 一般用于初始化操作中,除了项目初始化配置和goroutine之外几乎不会用
func ErrTest() {
_, err := os.OpenFile("Sakur.txt", os.O_WRONLY, 0666)
if err != nil {
log.Fatalln("读取配置文件失败,退出程序")
// log.Fatal和 panic一样
//panic("读取配置文件失败,退出程序")
}
}
3. 异常捕获
defer + recover 可以实现对 panic 的捕获
不至于让程序崩溃
func main() {
Test()
fmt.Println("执行下面的函数....")
}
func Test() {
// 利用defer + recover来捕获错误,defer后加上匿名函数的调用
defer func() {
err := recover()
// 如果没有捕获错误,返回值为零值nil
if err != nil {
fmt.Println("错误已经捕获")
fmt.Println("error是", err)
} else {
fmt.Println("没有错误需要捕获")
}
}()
num1 := 1
num2 := 1
result := num1 / num2
fmt.Println(result)
}
4. errors.Is()和errors.As()
4.1 erros.is
使用场景: 通过errors.is()
来判断具体的错误类型,来决定是不是需要打印错误日志
// errors.New 定义错误
var (
ErrZeroDivide = errors.New("zero divide")
ErrNoData = errors.New("no data")
)
func Zero(a, b int) error {
// 如果除数为0,返回错误
if b == 0 {
return ErrZeroDivide
} else {
return nil
}
}
func main() {
var a, b int
if err := Zero(a, b); err != nil {
// errors.is 判断错误类型
if errors.Is(err, ErrZeroDivide) {
fmt.Println("除0异常")
} else {
fmt.Println("no data")
}
}
}
从 Go 1.18 开始,Golang 官方建议用erross.is()替代 ==
4.2 errors.As
使用场景: 想要取得错误的具体信息 (传的什么值导致了错误)
// 定义错误
type ErrZeroDivide struct {
a int
b int
msg string
}
// 实现 error 接口
func (e ErrZeroDivide) Error() string {
return e.msg
}
//定义错误
type ErrNoData struct {
a int
b int
msg string
}
// 实现 error 接口
func (e ErrNoData) Error() string {
return e.msg
}
func Zero(a, b int) error {
if b == 0 {
err := ErrZeroDivide{
a: a,
b: b,
msg: "zero divide",
}
return err
} else {
return nil
}
}
func main() {
var a, b int
var errZero ErrZeroDivide
var errData ErrNoData
if err := Zero(a, b); err != nil {
// errors.As 除了判断类型,还可以取到具体的错误信息
// 这里将错误绑定到定义的结构体上
if errors.As(err, &errZero) {
fmt.Printf("除0异常,a=%d, b=%d ,msg=%s", errZero.a, errZero.b, errZero.msg)
} else {
fmt.Println("no data")
}
}
}
5. Errorf 格式化错误信息
// errors.New 定义错误
var (
ErrZeroDivide = errors.New("zero divide")
ErrNoData = errors.New("no data")
)
func Zero(a, b int) error {
if b == 0 {
// fmt.Errorf 格式化错误信息
return fmt.Errorf("mathFunc %w", ErrZeroDivide )
} else {
return nil
}
}
func main() {
var a, b int
if err := Zero(a, b); err != nil {
// errors.is 判断错误类型
// err == ErrZeroDivide
if errors.Is(err, ErrZeroDivide) {
fmt.Println("除0异常")
} else {
fmt.Println("no data")
}
}
}
如果使用 == 判断,那么格式化过的 err 是不等于 ErrZeroDivide 的,所以推荐用 errors.Is 判断错误