go的错误处理

一 error接口数组

        GO语言中的error类型其实是抽象Error()方法的error接口ide

            type error interface{函数

                    Error()    string
工具

             }测试

          GO语言使用该接口进行标准的错误处理。this

           对于大多数函数,若是要返回错误,大体上均可以定义为以下模式,将error做为多种返回值中的最后一个,但这并不是是强制要求:spa

            func Foo (param int) (n int,err error){设计

                // .....   code

            }
blog

            调用时的代码建议按照以下方式处理错误状况

            n,err := Foo(0)

            if err != nil {

                //错误处理

            }else{

                //使用返回值n

             }

        看下面的例子综合了一下error接口的用法:

        

package main

import(    
     "fmt"
    )
//自定义错误类型
type ArithmeticError struct {
    error   //实现error接口
}
//重写Error()方法
func (this *ArithmeticError) Error() string {
    return "自定义的error,error名称为算数不合法"
 }
 
 //定义除法运算函数
func Devide(num1, num2 int) (rs int, err error) { 
   if num2 == 0 { 
       return 0, &ArithmeticError{}
    } else {
       return num1 / num2, nil
    }
}
func main() { 
       var a, b int
    fmt.Scanf("%d %d", &a, &b)

    rs, err := Devide(a, b)
     if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("结果是:", rs)
    }
}

   

运行,输入参数5 2(正确的状况):

5 2结果是: 2

 

若输入5 0(产生错误的状况):

5 0自定义的error,error名称为算数不合法

 

        经过上面的例子能够看出error类型相似于Java中的Exception类型,不一样的是Exception必须搭配throw和catch使用。


2、defer--延迟语句

        在GO语言中,可使用关键字defer向函数注册退出调用,即主函数退出时,defer后的函数才会被调用。

        defer语句的做用是无论程序是否出现异常,均在函数退出时自动执行相关代码。(至关于Java中的finally )

当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。

例如:

复制代码

package main

import (    "fmt")

func main() {    for i := 0; i < 5; i++ {
        defer fmt.Println(i)
    }
}

复制代码

 

其执行结果为:

43210

 

defer语句在声明时被加载到内存(多个defer语句按照FIFO原则) ,加载时记录变量的值,而在函数返回以后执行,看下面的例子:

例子1:defer语句加载时记录值

复制代码

func f1() {
    i := 0
    defer fmt.Println(i) //其实是将fmt.Println(0)加载到内存
    i++    return}
func main() {
    f1()
}

复制代码

 

其结果显然是0

例子2:在函数返回后执行

复制代码

func f2() (i int) {    var a int = 1
    defer func() {
        a++
        fmt.Println("defer内部", a)
    }()    return a
}
func main() {
    fmt.Println("main中", f2())
}

复制代码

 

其结果是

 

defer内部 2main中 1

 

 

 

例子3:defer语句会读取主调函数的返回值,并对返回值赋值.(注意和例子2的区别)

复制代码

func f3() (i int) {
    defer func() {
        i++
    }()    return 1}
func main() {
    fmt.Println(f3())
}

复制代码

 

其结果居然是2.

经过上面的几个例子,天然而然会想到用defer语句作清理工做,释放内存资源(这样你不再会为Java中的try-catch-finally层层嵌套而苦恼了)

例如关闭文件句柄:

srcFile,err := os.Open("myFile")
defer srcFile.Close()

 

关闭互斥锁:

mutex.Lock()
defer mutex.Unlock()

 

上面例子中defer语句的用法有两个优势:

1.让设计者永远也不会忘记关闭文件,有时当函数返回时经常忘记释放打开的资源变量。

2.将关闭和打开靠在一块儿,程序的意图变得清晰不少。

下面看一个文件复制的例子:

复制代码

package main

import (    
        "fmt"
    "io"
    "os"
    )

func main() {
    copylen, err := copyFile("dst.txt", "src.txt")
        if err != nil { 
               return
    } else {
        fmt.Println(copylen)
    }

}
//函数copyFile的功能是将源文件sec的数据复制给dst
func copyFile(dstName, srcName string) (copylen int64, err error) {
    src, err := os.Open(srcName)
        if err != nil {
                return
    }
//当return时就会调用src.Close()把源文件关闭
    defer src.Close()
    dst, err := os.Create(dstName)
     if err != nil { 
          return
    }
       //当return是就会调用src.Close()把目标文件关闭
     defer dst.Close()
     return io.Copy(dst, src)
}

复制代码

 

能够看到确实比Java简洁许多。

三panic-recover运行时异常处理机制

Go语言中没有Java中那种try-catch-finally结构化异常处理机制,而使用panic()函数答题throw/raise引起错误,

而后在defer语句中调用recover()函数捕获错误,这就是Go语言的异常恢复机制——panic-recover机制

两个函数的原型为:

func panic(interface{})
//接受任意类型参数 无返回值
func recover() interface{}
//能够返回任意类型 无参数

 

必定要记住,你应当把它做为最后的手段来使用,也就是说,你的代码中应当没有,或者不多有panic的东西。这是个强大的工具,请明智地使用
它。那么,咱们应该如何使用它呢?

panic()
是一个内建函数,能够中断原有的控制流程,进入一个使人panic(恐慌即Java中的异常)的流程中。当函数F调用panic,函数F的执行被中
断,可是F中的延迟函数(必须是在panic以前的已加载的defer)会正常执行,而后F返回到调用它的地方。在调用的地方,F的行为就像调用了panic。这一
过程继续向上,直到发生panic的goroutine中全部调用的函数返回,此时程序退出。异常能够直接调用panic产
生。也能够由运行时错误产生,例如访问越界的数组。

recover()
是一个内建的函数,可让进入使人恐慌的流程中的goroutine恢复过来。recover仅在延迟函数中有效。在正常
的执行过程当中,调用recover会返回nil,而且没有其它任何效果。若是当前的goroutine陷入panic,调用
recover能够捕获到panic的输入值,而且恢复正常的执行。

通常状况下,recover()应该在一个使用defer关键字的函数中执行以有效截取错误处理流程。若是没有在发生异常的goroutine中明确调用恢复

过程(使用recover关键字),会致使该goroutine所属的进程打印异常信息后直接退出。

这里结合自定义的error类型给出一个使用panic和recover的完整例子:

复制代码

package main

import (    
"fmt"
)
//自定义错误类型
type ArithmeticError struct {
    error
}
//重写Error()方法
func (this *ArithmeticError) Error() string {
    return "自定义的error,error名称为算数不合法"
    }
//定义除法运算函数***这里与本文中第一幕①error接口的例子不一样
func Devide(num1, num2 int) int {    
    if num2 == 0 {
        panic(&ArithmeticError{}) 
        //固然也可使用ArithmeticError{}同时recover等到ArithmeticError类型
    } else {
            return num1 / num2
    }
}
func main() {
    var a, b int
    fmt.Scanf("%d %d", &a, &b)

    defer func() {
             if r := recover(); r != nil {
               fmt.Printf("panic的内容%v\n", r)
                 if _, ok := r.(error); ok {
                fmt.Println("panic--recover()获得的是error类型")
            }
            if _, ok := r.(*ArithmeticError); ok {
                fmt.Println("panic--recover()获得的是ArithmeticError类型")
            }
            if _, ok := r.(string); ok {
                fmt.Println("panic--recover()获得的是string类型")
            }
        }
    }()

    rs := Devide(a, b)
    fmt.Println("结果是:", rs)
}

复制代码

 

其执行的结果为:

使用与上面相同的测试数据,输入5 2得:

5 2结果是: 2

 

输入5 0得:

5 0panic的内容自定义的error,error名称为算数不合法
panic--recover()获得的是error类型
panic--recover()获得的是ArithmeticError类型

 

可见已将error示例程序转换为了Java中的用法,可是在大多数程序中使用error处理的方法较多。

须要注意的是:defer语句定义的位置 若是defer放在了

 rs := Devide(a, b)语句以后,defer将没有机会执行即:

复制代码

rs := Devide(a, b)
    defer func() {        
    if r := recover(); r != nil {
            fmt.Printf("panic的内容%v\n", r)            
            if _, ok := r.(error); ok {
                fmt.Println("panic--recover()获得的是error类型")
            }            if _, ok := r.(*ArithmeticError); ok {
                fmt.Println("panic--recover()获得的是ArithmeticError类型")
            }            if _, ok := r.(string); ok {
                fmt.Println("panic--recover()获得的是string类型")
            }
        }
    }()
相关文章
相关标签/搜索