go语言之错误异常详细示范(defer,recover的使用)

在go语言中,很多函数都提供有错误消息返回,比如常见的os库里的文件操作,当打开,读取等操作时都会触发错误。这些函数会返回多个值,其中就包括错误信息。还有一个,当判断map类型中某个key是否存在时,也可以用到多返回值。比如:

    stud:=make(map[int]string)
    stud[2]="haha" //插入key-value
    stud[5]="jack"
    stud[3]="xia"
    retvalue,bfind:=stud[4] //这里就是多返回值,retvalue是要查找的key对应值,bfind是一个bool型变量表示查找结果
    if bfind {
        fmt.Printf("找到了,stud[4]=%s\n",retvalue)
    }else {
        fmt.Println("没有找到stud[4]")
    }

下面还以代码示例,go语言中错误异常的一些写法,这个例子是演示了go语言中defer声明的使用,在它后面所跟的语句,不论是否有异常,都会在最后执行。

package main

import "os"
import "fmt"
import "time"

func main() {
    //调用os.Open方法打开文件
    var f, err = os.Open("1.txt")
    if err != nil {
        // 文件不存在输出具体错误信息,err变量是一个error类型对象,内置的Error()方法是获取每次的具体错误信息
        fmt.Println("open file failed:" + err.Error())
        return
    }
    time.Sleep(1 * time.Second)//延时等待1秒
    //这里defer就是相当于python语言中错误异常的finally,不论是否出错异常都会执行。   

   defer func (ir int) { //这里我们使用了匿名函数,格式:func (传入参数) (返回值) {函数体},这里是需要传递一个int变量,并输出来。
        fmt.Println("close file:",ir) //显示结果信息和传入的参数,在程序的最后会打印出这句话*********
        f.Close() //关闭打开的文件句柄
    }(88)//这里(88)就是相当于调用匿名函数,因为这里是要传入一个参数。如传入参数为空,就写()。

    var filedata = []byte{} //文件或网络数据一般都是byte类型,这里定义了一个不定长度数组来存放文件数据。
    
    var buf = make([]byte, 1000)//定义一个切片,动态数组。使用make函数创建,第二个参数就是数组的长度。
    for {
        // 读文件,将读到的内容填充到缓冲
        n, err := f.Read(buf)
        if n > 0 {

//append 函数参数中出现了 … 符号,它的作用是将切片参数的指定元素展开后传递给 append 函数,这里是把buf的所有元素传入。
            
            filedata = append(filedata, buf[:n]...)
        }
        if err != nil {
            // 读取到最后,会把文件末尾标志符EOF读出,如不想输出该结束符则忽略
            if err.Error() != "EOF" {
                fmt.Println("Read file failed:" + err.Error())
            }
            break
        }
    }
    // 输出文件内容
    fmt.Println(string(filedata))
}

上边的例子是对库函数(Open,Read等文件操作)本身调用时,产生了一些错误异常,如何打印出错误信息的演示。

下面介绍下如何对程序中其他异常崩溃进行处理,比如我们在写程序时不小心数组访问越界,或者除法的除数为0等情况,这时需要使用recover函数来获取捕获这些异常,它就是用来保护检测下面代码块的,需要放到defer模块语句里。而当程序中定义了多个defer语句时,按照先声明后调用的顺序依次执行,就跟c++析构函数调用顺序一样。

上代码演示:

package main

import "fmt"

func main() {
    taray:=[]int{1,2,3,4,5}
    defer func() {//这里是定义了一个匿名函数,
        var err = recover() //recover就是捕获下面程序块中的错误信息,相当于c++中的try/catch,把下面数组遍历代码块用try包含起来
        if err != nil {//如捕获到的错误对象不为空,说明就是有错误了呗!
            fmt.Println("遍历数组捕获到异常:",err)//打印出错误信息
            return //然后就返回了
        }
        fmt.Println("遍历数组结束")//这里是如没有错误信息时的处理。要等到整个程序最后结束时运行。
    }()        
    for i:=0;i<5;i++ {//这里是测试捕获异常,专门定义了超过数组大小的越界访问。
        fmt.Printf("第%d个元素是:%d\n",i+1,taray[i])
    }
    t:=2
    defer func() {
        err := recover() //这里的recover是捕获下面除法运算的代码,起到捕获异常的作用。
        if err != nil {
            fmt.Println("除法运算捕获到异常:",err)
            return
        }
        fmt.Println("除法运算结束")//在程序中有多个defer语句,按照先声明后调用,后声明先调用顺序。类似c++中声明对象析构函数一样。所以这里它先打印。
    }()    
    fmt.Println(taray[3]/t)
}
第一次,我们都是按正确的写法,上边数组遍历,我们下标不超过5。下边的除法除数不等于0,结果如下:

可以看到,程序运行正常,最后是先打印出第2个defer语句里的除法运算信息,然后才是第1个语句里的遍历数组信息。说明,当程序里有多个defer语句时,他们在结束时按照先声明后调用的顺序。

第二次,我们把数组下标越界,测试如下:

第一个defer语句里的recover函数获取到了错误信息。

第三次,我们恢复数组正常访问,把下面除数改为0,测试如下:

可以看到第2个defer里的recover捕获到了这个除数0的错误信息,虽然它return返回了,但是在程序最后是调用了第1个defer里的打印信息。

总结:》》》我们今后如在操作一些文件句柄或其他i/o句柄时,都要加上defer语句,先在里面用recover捕获可能出现的异常错误,然后再写上释放句柄的函数,确保程序在发生意外时资源也能有效释放。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值