Go语言学习之异常处理-DateWhale开源学习

目录

error

Go语言内置了一个简单的错误接口作为一种错误处理机制,接口定义如下:http://golang.org/pkg/builtin/#error

type error interface {
	Error() string
}

它包含一个 Error() 方法,返回值为string

Goerror构造有两种方式,分别是

第一种:errors.New()
http://golang.org/src/pkg/errors/errors.go

err := errors.New("This is an error")
if err != nil {
  fmt.Print(err)
}

errors.New()返回的是内部errorString对象的指针。

第二种:fmt.Errorf():
http://golang.org/src/pkg/errors/errors.go

err := fmt.Errorf("This is an error")
if err != nil {
  fmt.Print(err)
}

除了直接使用Go自带的方法,还可以自定义错误。下面以自然数函数作为例子:

type NotNature float64

func (err NotNature) Error() string {
 return fmt.Sprintf("自然数为大于或等于0的数: %v", float64(err))
}

func Nature(x float64) (float64,error) {
  if x<0 {
   return 0,NotNature(x)
  } else {
  return x,nil
  }
}

func main() {
  fmt.Println(Nature(1))
  fmt.Println(Nature(-1))
}

需要注意一下几点:

  • 如果函数需要处理异常,通常将error作为多值返回的最后一个值,返回的error值为nil则表示无异常,非nil则是有异常。

  • 一般先用if语句处理error!=nil,正常逻辑放if后面。

Go语言的error代表的并不是真“异常”,只是通过返回error来表示错误信息,换句话说,不是运行时错误范围预定义的错误,某种不符合期望的行为并不会导致程序无法运行(自然数函数例子),都应使用error进行异常处理。当程序出现重大错误,如数组越界,才会将其当成真正的异常,并用panic来处理。

定义自己的错误

实现error接口

package main

import (
    "fmt"
    "time"
)

// MyError is an error implementation that includes a time and message.
type MyError struct {
    When time.Time
    What string
}

func (e MyError) Error() string {
    return fmt.Sprintf("%v: %v", e.When, e.What)
}

func oops() error {
    return MyError{
        time.Date(1989, 3, 15, 22, 30, 0, 0, time.UTC),
        "the file system has gone away",
    }
}

func main() {
    if err := oops(); err != nil {
        fmt.Println(err)
    }
}

panic

Go不使用try...catch方法来处理异常,而是使用panicrecover

先上代码举一个简单的例子

func main() {
  fmt.Println("Hello,Go!")
  panic(errors.New(" i am a error"))
  fmt.Println("hello,again!")
}

输出:

Hello,Go!
panic:  i am a error
goroutine 1 [running]:
main.main()
	~/error.go:12 +0xb5
exit status 2

可以看到,panic后面的程序不会被执行了。但是我们捕捉异常并不是为了停止程序(一般情况),而是为了让程序能正常运行下去,这时候就到recover出场了。

package main

import "fmt"

func main(){
  defer func(){
    fmt.Println("我是defer里面第一个打印函数")
    if err:=recover();err!=nil{
        fmt.Println(err)
    }
    fmt.Println("我是defer里面第二个打印函数")
  }()
  f()
}

func f(){
  fmt.Println("1")
  panic("我是panic")
  fmt.Println("2")
}

输出:

1
我是defer里面第一个打印函数
我是panic
我是defer里面第二个打印函数

可以看到,f函数一开始正常打印,当遇到panic,就跳到defer函数,执行defer函数里的内容

需要注意的是,defer函数里打印的err其实就是panic里面的内容。
下面详细介绍一下panicrecover的原理。首先来看一下panicrecover的官方定义

func panic(v interface{})

内置函数panic会停止当前goroutine的正常执行。当函数F调用panic时,F的正常执行立即停止。任何被F延迟执行的函数都将以正常的方式运行,然后F返回其调用者。对调用方G来说,对F的调用就像调用panic一样,终止G的执行并运行任何延迟的函数。直到执行goroutine中的所有函数都按逆序停止。此时,程序将以非0退出代码终止。此终止序列称为panicking,可由内置函数recover控制。

func recover() interface{}

recover内置函数允许程序管理panickinggoroutine的行为。在defer函数(但不是它调用的任何函数)内执行恢复调用,通过恢复正常执行来停止panicking序列,并检索传递给panic调用的错误值。如果在defer函数之外调用recover,则不会停止panicking的序列。在这种情况下,或者当goroutinepanicking时,或者提供给panic的参数是nilrecover返回nil。因此,recover的返回值报告goroutine是否panicking

注意:deferrecover必须在panic之前定义,否则无效。

源码分析

errors.New的定义如下

// src/errors/errors.go

// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
	return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
	s string
}

func (e *errorString) Error() string {
	return e.s
}
  • New函数返回格式为给定文本的错误

  • 即使文本是相同的,每次对New的调用都会返回一个不同的错误值。

Error vs Exception

各个语言的演进历史:

  • C
    单返回值,一般通过传递指针作为指针为入参,返回值为int表示成功还是失败
  • C++
    引入了exception,但是无法知道被调用方法会抛出什么异常。
  • Java
    引入了checked exception,方法的所有者必须申明,调用者必须处理。在启动时抛出大量的异常是司空见惯的事情,并在它们的调用堆栈中尽职地记录下来。Java异常不再是异常,而是变得司空见惯了。它们从良性到灾难性都有使用,异常的严重性由函数的调用者来区分。

Go的处理异常逻辑是不引入exception,支持多参数返回,所以你很容易的在函数签名中带上实现了error interface的对象,交由调用者来判定。
如果一个函数返回了value, error,你不能对这个value做任何假设,必须先判定error。唯一可以忽略error 的是,如果你连value也不关心
Go中有panic的机制,如果你认为和其他语言的exception一样,那你就错了。当我们抛出异常的时候,相当于你把exception扔给了调用者来处理。
C++中,把string转为int,如果转换失败,会抛出异常。或者在java中转换stringdate失败时,会抛出异常。
Go panic意味着fatal error(就是挂了)。不能假设调用者来解决panic,意味着代码不能继续运行。

使用多个返回值和一个简单的约定,Go解决了让程序员知道什么时候出了问题,并为真正的异常情况保留了panic

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值